Heatmap of Pairwise Common Substitutions of all Tumor Samples

#moleID <- c('288555')
moleID <- c('297368')
library(RMySQL)
library(repr)
library(ggplot2)
library(reshape2)
library(plyr)
library(dplyr)
library(DT)
mysqlQuery <- function (query) {
  DB <- dbConnect(MySQL(), user='ashlien', password='Re4Cna$c', dbname='clinicalC', host='172.27.20.20', port=5029)
  rs <- dbSendQuery(DB, query)
  result <- fetch(rs, -1)
  dbDisconnect(DB)
  return(result)
}
shareSNV <- mysqlQuery("SELECT CONCAT_WS('_', si.sampleID,si.pairID) as sampleID,vc.interID FROM variants_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name cross join (select vc.interID,count(*) from variants_cancer as vc inner join sampleInfo as si on si.postprocID = vc.tumor_name where si.pairID > 0 and si.pairID < 9900 AND (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream'))) group by vc.interID HAVING count(*) > 1) tmp where tmp.interID = vc.interID AND si.pairID > 0 and si.pairID < 9900 and (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream')))")
shareSNV$sampleID <- sub(moleID[1], paste('>>', moleID[1]), shareSNV$sampleID)
sampleSNV <- mysqlQuery("SELECT CONCAT_WS('_', si.sampleID,si.pairID) as sampleID, count(*) as count FROM variants_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name WHERE si.pairID > 0 AND si.pairID < 9900 AND (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream'))) group by sampleID")
sampleSNV$sampleID <- sub(moleID[1], paste('>>', moleID[1]), sampleSNV$sampleID)
heat <- matrix(0, length(unique(sampleSNV$sampleID)), length(unique(sampleSNV$sampleID)))
colnames(heat) <- unique(sampleSNV$sampleID)
rownames(heat) <- unique(sampleSNV$sampleID)
for (i in 1:length(sampleSNV$sampleID)) {
  heat[sampleSNV$sampleID[i], sampleSNV$sampleID[i]] = sampleSNV$count[i]
}
for ( val1 in 1:(length(shareSNV$sampleID)-1) ) { for (val2 in (val1+1):length(shareSNV$sampleID)) { if (shareSNV$interID[val1] == shareSNV$interID[val2]) { heat[shareSNV$sampleID[val1],  shareSNV$sampleID[val2]] = heat[shareSNV$sampleID[val1],  shareSNV$sampleID[val2]] + 1 ; heat[shareSNV$sampleID[val2],  shareSNV$sampleID[val1]] = heat[shareSNV$sampleID[val2],  shareSNV$sampleID[val1]] + 1} }}
heat.m <- melt(heat)
ggplot(heat.m, aes(Var1, Var2, fill = value)) + geom_tile(aes(fill = value)) +  geom_text(data=subset(heat.m, value > 0), aes(label = value), size = 1.5) + scale_fill_gradient(low = "white", high = "red") + ylab('SampleID_KiCS') + xlab("SampleID_KiCS") + theme(axis.text=element_text(size=6), axis.text.x = element_text(angle = 90, hjust = 1))

Heatmap of Pairwise Common INDELs of all Tumor Samples

shareINDEL <- mysqlQuery("SELECT CONCAT_WS('_', si.sampleID,si.pairID) as sampleID,vc.interID FROM indel_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name cross join (select vc.interID,count(*) from indel_cancer as vc inner join sampleInfo as si on si.postprocID = vc.tumor_name where si.pairID > 0 and si.pairID < 9900 AND (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter NOT LIKE 'alt_allele_in_normal' AND gatk_filter NOT LIKE 'germline_risk' AND gatk_filter NOT LIKE 'multi_event_alt_allele_in_normal' AND NOT (gatk_filter LIKE 'clustered_events' AND gatk_filter LIKE 'homologous_mapping_event')))) group by vc.interID HAVING count(*) > 1) tmp where tmp.interID = vc.interID AND si.pairID > 0 and si.pairID < 9900 and (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter NOT LIKE 'alt_allele_in_normal' AND gatk_filter NOT LIKE 'germline_risk' AND gatk_filter NOT LIKE 'multi_event_alt_allele_in_normal' AND NOT (gatk_filter LIKE 'clustered_events' AND gatk_filter LIKE 'homologous_mapping_event'))))")
shareINDEL$sampleID <- sub(moleID[1], paste('>>', moleID[1]), shareINDEL$sampleID)
sampleINDEL <- mysqlQuery("SELECT CONCAT_WS('_', si.sampleID,si.pairID) as sampleID, count(*) as count FROM indel_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name WHERE si.pairID > 0 AND si.pairID < 9900 AND (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter NOT LIKE 'alt_allele_in_normal' AND gatk_filter NOT LIKE 'germline_risk' AND gatk_filter NOT LIKE 'multi_event_alt_allele_in_normal' AND NOT (gatk_filter LIKE 'clustered_events' AND gatk_filter LIKE 'homologous_mapping_event')))) group by sampleID")
sampleINDEL$sampleID <- sub(moleID[1], paste('>>', moleID[1]), sampleINDEL$sampleID)
heat <- matrix(0, length(unique(sampleINDEL$sampleID)), length(unique(sampleINDEL$sampleID)))
colnames(heat) <- unique(sampleINDEL$sampleID)
rownames(heat) <- unique(sampleINDEL$sampleID)
for (i in 1:length(sampleINDEL$sampleID)) {
  heat[sampleINDEL$sampleID[i], sampleINDEL$sampleID[i]] = sampleINDEL$count[i]
}
for ( val1 in 1:(length(shareINDEL$sampleID)-1) ) { for (val2 in (val1+1):length(shareINDEL$sampleID)) { if (shareINDEL$interID[val1] == shareINDEL$interID[val2]) { heat[shareINDEL$sampleID[val1],  shareINDEL$sampleID[val2]] = heat[shareINDEL$sampleID[val1],  shareINDEL$sampleID[val2]] + 1 ; heat[shareINDEL$sampleID[val2],  shareINDEL$sampleID[val1]] = heat[shareINDEL$sampleID[val2],  shareINDEL$sampleID[val1]] + 1} }}
heat.m <- melt(heat)
ggplot(heat.m, aes(Var1, Var2, fill = value)) + geom_tile(aes(fill = value)) +  geom_text(data=subset(heat.m, value > 0), aes(label = value), size = 1.5) + scale_fill_gradient(low = "white", high = "red") + ylab('SampleID_KiCS') + xlab("SampleID_KiCS") + theme(axis.text=element_text(size=6), axis.text.x = element_text(angle = 90, hjust = 1))

Overall view of nonsysnonymous genes

all_Nonsynon = mysqlQuery("SELECT * from (SELECT si.sampleID, vc.hgnc_gene FROM indel_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name WHERE si.pairID > 0 AND si.pairID < 9900 AND (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR ((gatk_filter NOT LIKE 'clustered_events' OR gatk_filter NOT LIKE 'homologous_mapping_event') AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01))) AND annovar_exonic_func REGEXP 'frameshift deletion|frameshift insertion|nonframeshift deletion|nonframeshift insertion|stopgain SNV|stoploss SNV' UNION SELECT si.sampleID, vc.hgnc_gene FROM variants_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name WHERE si.pairID > 0 AND si.pairID < 9900 AND (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream'))) AND annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing') tmp GROUP by sampleID,hgnc_gene")
Closing open result sets
allGenes <- matrix(NA, length(unique(all_Nonsynon$hgnc_gene)), length(unique(all_Nonsynon$sampleID)))
colnames(allGenes) <- unique(all_Nonsynon$sampleID)
rownames(allGenes) <- unique(all_Nonsynon$hgnc_gene)
for (i in 1:length(all_Nonsynon$sampleID)) {
  allGenes[all_Nonsynon$hgnc_gene[i], all_Nonsynon$sampleID[i]] <- TRUE
}
order_names <- unique(all_Nonsynon$sampleID)
order_names <- order_names[ ! order_names %in% moleID ]
order_names <- c(moleID[1], order_names)
allGenes <- allGenes[,order_names]
allGenes <- cbind(allGenes, Count = apply(allGenes, 1, function(x) sum(!is.na(x))))
allGenes <- allGenes[ which(allGenes[, 'Count'] > "1"), ]
allGenes <- allGenes[ order(-allGenes[,ncol(allGenes)]),]
datatable(allGenes) %>% formatStyle(moleID,  color = 'red', backgroundColor = 'orange', fontWeight = 'bold')
allGenes <- melt(allGenes)
allGenes <- allGenes[ which(allGenes$value == 1), ]
allGenes[ which(allGenes$Var2 == moleID[1]),]$value <- 2
allGenes$value <- factor(allGenes$value, levels = allGenes$value) 
allGenes %>% ggplot(aes(Var2, Var1))  + geom_point(shape = 4, aes(color=value)) + theme(axis.text=element_text(size=6), axis.text.x = element_text(angle = 90, hjust = 1)) + theme(legend.position="none")

#+ facet_wrap(  ~ Gene_Cvg, scales = "free") + ggtitle(paste('Coverage of all exons in the cancer panel for sample ', moleID[1], sep='')) + theme(plot.title = element_text(hjust = 0.5), plot.margin = unit(c(0.1, 0, 0, -0.8), "line"), panel.spacing = unit(0, "lines"), strip.text = element_text(size=5, hjust = 0), strip.background = element_rect(fill = "white", color = "white", size = 2)) + theme(axis.line.y = element_blank(), axis.ticks.y = element_blank(), strip.text.y = element_text(angle = 0)) + theme(axis.line.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x  = element_blank(), axis.text.y  = element_blank()) + xlab(paste("Average Coverage is ", cvgSum$meanCvgGP[1], "X, (w/ ", cvgSum$perbasesAbove30XGP[1], "% bases > 500X)\n", "Exons Coverage w/ ", nrow(covs[covs$good_cov == TRUE,]) , " > 50X, ", nrow(covs[covs$bad_cov == TRUE,]), " < 50X", sep = ''))

Current Investigated sample and the nonsynonymous Mutations on the same gene

rows = function(tab) lapply(
  seq_len(nrow(tab)),
  function(i) unclass(tab[i,,drop=F])
)
cmdQuery <- paste("SELECT vc.hgnc_gene, vc.annovar_chr, vc.annovar_start, vc.annovar_end, vc.mutationType, vc.annovar_func, vc.annovar_ens_func, vc.aa, vc.cosmic_census, si.sampleID, ip.interpretation, vc.tumor_f,  CONCAT_WS('_', vc.interID, vc.ref_allele, vc.alt_allele) as newinterID FROM variants_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name LEFT JOIN interpretation_cancer as ip on ip.postprocID = vc.tumor_name AND ip.interID = vc.interID CROSS JOIN ( SELECT vc.ensembl_gene FROM variants_cancer as vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name WHERE si.sampleID = '", moleID[1], "' AND (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream')))) AS tmp WHERE (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream'))) AND si.pairID > 0 AND si.pairID < 9900 AND ((vc.ensembl_gene = tmp.ensembl_gene AND vc.annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing') OR si.sampleID = '", moleID[1], "') GROUP by vc.interID,tumor_name", collapse = "", sep = '')
curInvestSNV <- mysqlQuery(cmdQuery)
interTrans <- c("7" = "Unknown", "6" = "Class-E", "5" = "Class-D", "4" = "Class-C", "3" = "Class-B", "2" = "Class-A", "1" = "Unknown", "0" = "Unknown")
curInvestSNV$interpretation <- as.character(curInvestSNV$interpretation)
curInvestSNV$interpretation <- revalue(curInvestSNV$interpretation, interTrans)
The following `from` values were not present in `x`: 7, 6, 5, 4, 2, 1
curInvestSNV$cosmic_census[curInvestSNV$cosmic_census == 1] <- "T"
curInvestSNV$cosmic_census[curInvestSNV$cosmic_census == 0] <- "F"
curInvestSNV$Mutation <- NULL
index <- curInvestSNV$aa == ""
curInvestSNV$Mutation[index] <- paste(curInvestSNV$annovar_chr[index], ":", curInvestSNV$annovar_start[index], "-", curInvestSNV$annovar_end[index] , " (", curInvestSNV$mutationType[index], ")<br/>func: ", curInvestSNV$annovar_func[index], "<br/>ens func: ", curInvestSNV$annovar_ens_func[index], sep = '')
index <- curInvestSNV$aa != ""
curInvestSNV$Mutation[index] <- paste(curInvestSNV$annovar_chr[index], ":", curInvestSNV$annovar_start[index], "-", curInvestSNV$annovar_end[index] , " (", curInvestSNV$mutationType[index], ")<br/>func: ", curInvestSNV$annovar_func[index], "<br/>ens func: ", curInvestSNV$annovar_ens_func[index], "<br/>AA: ", curInvestSNV$aa[index], sep = '')
curInvestSNV$MuType <- "substitution"
otherSNV <- curInvestSNV[ which(curInvestSNV$sampleID != moleID[1]), ]
otherSNV <- otherSNV[order(otherSNV$sampleID), ]
currentSNV = curInvestSNV[ which(curInvestSNV$sampleID == moleID[1]), ]
snvPrint <- currentSNV[c(1, 15, 14, 11,9,12)]
snvPrint$other <- ""
for (i in rows(otherSNV)) {
  snvPrint$other[ snvPrint$hgnc_gene == i$hgnc_gene ] <- paste(i$aa, "(", i$sampleID, "), ", snvPrint$other[ snvPrint$hgnc_gene == i$hgnc_gene ], sep = "")
}
cmdQuery <- paste("SELECT vc.hgnc_gene, vc.annovar_chr, vc.annovar_start, vc.annovar_end, vc.annovar_ref, vc.annovar_alt, vc.gatk_mutation_type as mutationType, vc.annovar_func, vc.annovar_ens_func, vc.aa, vc.cosmic_census, si.sampleID, ip.interpretation, vc.gatk_tumour_allele_fraction FROM indel_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name LEFT JOIN interpretation_cancer as ip on ip.postprocID = vc.tumor_name AND ip.interID = vc.interID CROSS JOIN ( SELECT vc.ensembl_gene FROM indel_cancer as vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name WHERE si.sampleID = '", moleID[1] ,"' AND (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter != 'PASS' AND gatk_filter NOT LIKE '%clustered_events%' AND gatk_filter NOT LIKE '%homologous_mapping_event%' AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_alt_count >= 5)))) AS tmp WHERE (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter != 'PASS' AND gatk_filter NOT LIKE '%clustered_events%' AND gatk_filter NOT LIKE '%homologous_mapping_event%' AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_alt_count >= 5))) AND si.pairID > 0 AND si.pairID < 9900 AND ((vc.ensembl_gene = tmp.ensembl_gene AND vc.annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing') OR si.sampleID = '", moleID[1] ,"') GROUP by vc.interID,tumor_name", collapse = "", sep = '')
curInvestINDEL <- mysqlQuery(cmdQuery)
curInvestINDEL$interpretation <- as.character(curInvestINDEL$interpretation)
curInvestINDEL$interpretation <- revalue(curInvestINDEL$interpretation, interTrans)
The following `from` values were not present in `x`: 7, 6, 5, 4, 3, 2, 1, 0
curInvestINDEL$cosmic_census[curInvestINDEL$cosmic_census == 1] <- "T"
curInvestINDEL$cosmic_census[curInvestINDEL$cosmic_census == 0] <- "F"
curInvestINDEL$Mutation <- NULL
index <- curInvestINDEL$aa == ""
curInvestINDEL$Mutation[index] <- paste(curInvestINDEL$annovar_chr[index], ":", curInvestINDEL$annovar_start[index], "-", curInvestINDEL$annovar_end[index] , " ( ", curInvestINDEL$annovar_ref, " > ", curInvestINDEL$annovar_alt, " )<br/>func: ", curInvestINDEL$annovar_func[index], "<br/>ens func: ", curInvestINDEL$annovar_ens_func[index], sep = '')
index <- curInvestINDEL$aa != ""
curInvestINDEL$Mutation[index] <- paste(curInvestINDEL$annovar_chr[index], ":", curInvestINDEL$annovar_start[index], "-", curInvestINDEL$annovar_end[index] , " ( ", curInvestINDEL$annovar_ref, " > ", curInvestINDEL$annovar_alt, " )<br/>func: ", curInvestINDEL$annovar_func[index], "<br/>ens func: ", curInvestINDEL$annovar_ens_func[index], "<br/>AA: ", curInvestINDEL$aa[index], sep = '')
indelTrans <- c("ins" = "insertion", "del" = "deletion")
curInvestINDEL$mutationType <- revalue(curInvestINDEL$mutationType, indelTrans)
otherINDEL = curInvestINDEL[ which( curInvestINDEL$sampleID != moleID[1]), ]
currentINDEL = curInvestSNV[ which(curInvestSNV$sampleID == moleID[1]), ]
indelPrint <- curInvestINDEL[c(1,7,15,13,11,14)]
indelPrint$other <- ""
for (i in rows(otherINDEL)) {
  indelPrint$other[ indelPrint$hgnc_gene == i$hgnc_gene ] <- paste(i$aa, "(", i$sampleID, "), ", indelPrint$other[ indelPrint$hgnc_gene == i$hgnc_gene ], sep = "")
}
printTable <- as.data.frame(mapply(c, snvPrint, indelPrint ))
datatable(printTable, escape = 2, rownames = FALSE,  colnames = c('Gene', 'MutationType', 'Mutation', 'Interpretation', 'Cancer Gene', 'VAF', 'Mutation on Others'))

Common substitutions between current investigated sample and other samples:

cmdQuery <- paste("SELECT vc.hgnc_gene, vc.annovar_chr, vc.annovar_start, vc.annovar_end, vc.mutationType, vc.annovar_func, vc.annovar_ens_func, vc.aa, vc.cosmic_census, si.sampleID, ip.interpretation, vc.tumor_f, vc.interID, CONCAT_WS('_', vc.interID, vc.ref_allele, vc.alt_allele) as newinterID FROM variants_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name LEFT JOIN interpretation_cancer as ip on ip.postprocID = vc.tumor_name AND ip.interID = vc.interID cross join (select vc.interID,count(*) from variants_cancer as vc inner join sampleInfo as si on si.postprocID = vc.tumor_name cross join (SELECT vc.interID from variants_cancer as vc inner join sampleInfo as si on si.postprocID = vc.tumor_name where (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream'))) AND si.sampleID in ('", moleID[1], "' )) as su where su.interID = vc.interID AND si.pairID > 0 and si.pairID < 9900 AND (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream'))) group by vc.interID HAVING count(*) > 1) tmp where tmp.interID = vc.interID AND si.pairID > 0 and si.pairID < 9900 and (((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%clin%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%clin%') AND judgement = 1) OR (( annovar_1000g is NULL OR annovar_1000g < 0.01 ) AND ( annovar_esp is NULL OR annovar_esp < 0.01) AND t_alt_count >= 10 AND  n_alt_count < 3 AND n_ref_count > 50 AND tumor_f > 0.01 AND cosmic_census = 1 AND (annovar_exonic_func REGEXP 'nonsynonymous SNV|stopgain SNV|stoploss SNV|splicing' OR  annovar_func REGEXP 'splicing|upstream')))", collapse = "", sep = '')
commonSNVs <- mysqlQuery(cmdQuery)
interTrans <- c("7" = "Unknown", "6" = "Class-E", "5" = "Class-D", "4" = "Class-C", "3" = "Class-B", "2" = "Class-A", "1" = "Unknown", "0" = "Unknown")
commonSNVs$interpretation <- as.character(commonSNVs$interpretation)
commonSNVs$interpretation <- revalue(commonSNVs$interpretation, interTrans)
The following `from` values were not present in `x`: 7, 6, 5, 4, 3, 2, 1
commonSNVs$cosmic_census[commonSNVs$cosmic_census == 1] <- "T"
commonSNVs$cosmic_census[commonSNVs$cosmic_census == 0] <- "F"
commonSNVs$Mutation <- NULL
index <- commonSNVs$aa == ""
commonSNVs$Mutation[index] <- paste(commonSNVs$annovar_chr[index], ":", commonSNVs$annovar_start[index], "-", commonSNVs$annovar_end[index] , " (", commonSNVs$mutationType[index], ")<br/>func: ", commonSNVs$annovar_func[index], "<br/>ens func: ", commonSNVs$annovar_ens_func[index], sep = '')
index <- commonSNVs$aa != ""
commonSNVs$Mutation[index] <- paste(commonSNVs$annovar_chr[index], ":", commonSNVs$annovar_start[index], "-", commonSNVs$annovar_end[index] , " (", commonSNVs$mutationType[index], ")<br/>func: ", commonSNVs$annovar_func[index], "<br/>ens func: ", commonSNVs$annovar_ens_func[index], "<br/>AA: ", commonSNVs$aa[index], sep = '')
commonSNVs$MuType <- "substitution"
printCommonSNVs <- commonSNVs[ which(commonSNVs$sampleID == moleID[1]), ]
otherCommonSNVs <- commonSNVs[ which(commonSNVs$sampleID != moleID[1]), ]
sidFreq <- data.frame(table(otherCommonSNVs$sampleID))
sidFreq <- sidFreq[order(-sidFreq$Freq), ]
names(sidFreq)[1] <- "sampleID"
sidFreq$sampleID <- as.character(sidFreq$sampleID)
numcol <- 10
if (length(sidFreq$sampleID) < 10) {
    numcol <- length(sidFreq$sampleID)
}
finalcolnames <- c('Gene', 'MutationType', 'Mutation', 'Interpretation', 'Cancer Gene', 'VAF')
for (i in 1:numcol) {
  index1 <- otherCommonSNVs$sampleID == sidFreq$sampleID[i]
  tempid <- paste("sampleID", i , sep = '')
  finalcolnames <- append(finalcolnames, tempid)
  printCommonSNVs[tempid] <- NA
  printCommonSNVs[[tempid]][printCommonSNVs$interID %in% otherCommonSNVs$interID[index1]] <- sidFreq$sampleID[i]
}
printCommonSNVs$sampleCount <- apply(printCommonSNVs[17:(16+numcol)], 1, function(x) sum(!is.na(x)))
finalcolnames <- append(finalcolnames, "sampleCount")
printCommonSNVs <- printCommonSNVs[c(1, 16, 15, 11,9,12, 17:(17+numcol))]
datatable(printCommonSNVs, escape = 2, rownames = FALSE,  colnames = finalcolnames)

Common INDELs between current investigated sample and other samples:

cmdQuery <- paste("SELECT vc.hgnc_gene, vc.annovar_chr, vc.annovar_start, vc.annovar_end, vc.gatk_mutation_type, vc.annovar_func, vc.annovar_ens_func, vc.aa, vc.cosmic_census, si.sampleID, ip.interpretation, vc.gatk_tumour_lod, vc.interID, CONCAT_WS('_', vc.interID, vc.annovar_ref, vc.annovar_alt) as newinterID FROM indel_cancer AS vc INNER JOIN sampleInfo AS si ON si.postprocID = vc.tumor_name LEFT JOIN interpretation_cancer as ip on ip.postprocID = vc.tumor_name AND ip.interID = vc.interID cross join (select vc.interID,count(*) from indel_cancer as vc inner join sampleInfo as si on si.postprocID = vc.tumor_name cross join (SELECT vc.interID from indel_cancer as vc inner join sampleInfo as si on si.postprocID = vc.tumor_name where (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter NOT LIKE 'alt_allele_in_normal' AND gatk_filter NOT LIKE 'germline_risk' AND gatk_filter NOT LIKE 'multi_event_alt_allele_in_normal' AND NOT (gatk_filter LIKE 'clustered_events' AND gatk_filter LIKE 'homologous_mapping_event')))) AND si.sampleID in ('", moleID[1], "' )) as su where su.interID = vc.interID AND si.pairID > 0 and si.pairID < 9900 AND (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter NOT LIKE 'alt_allele_in_normal' AND gatk_filter NOT LIKE 'germline_risk' AND gatk_filter NOT LIKE 'multi_event_alt_allele_in_normal' AND NOT (gatk_filter LIKE 'clustered_events' AND gatk_filter LIKE 'homologous_mapping_event')))) group by vc.interID HAVING count(*) > 1) tmp where tmp.interID = vc.interID AND si.pairID > 0 and si.pairID < 9900 and (gatk_mutation_type != 'sub' AND gatk_normal_depth >= 30 AND gatk_tumour_depth >= 30 AND gatk_tumour_alt_count >= 5 AND gatk_normal_allele_fraction < 0.01 AND gatk_tumour_allele_fraction >= 0.05 AND (gatk_filter = 'PASS' OR (gatk_filter NOT LIKE 'alt_allele_in_normal' AND gatk_filter NOT LIKE 'germline_risk' AND gatk_filter NOT LIKE 'multi_event_alt_allele_in_normal' AND NOT (gatk_filter LIKE 'clustered_events' AND gatk_filter LIKE 'homologous_mapping_event'))))", collapse = "", sep = '')
commonSNVs <- mysqlQuery(cmdQuery)
interTrans <- c("7" = "Unknown", "6" = "Class-E", "5" = "Class-D", "4" = "Class-C", "3" = "Class-B", "2" = "Class-A", "1" = "Unknown", "0" = "Unknown")
commonSNVs$interpretation <- as.character(commonSNVs$interpretation)
commonSNVs$interpretation <- revalue(commonSNVs$interpretation, interTrans)
The following `from` values were not present in `x`: 7, 6, 5, 4, 3, 2, 1, 0
commonSNVs$cosmic_census[commonSNVs$cosmic_census == 1] <- "T"
commonSNVs$cosmic_census[commonSNVs$cosmic_census == 0] <- "F"
commonSNVs$Mutation <- NULL
index <- commonSNVs$aa == ""
commonSNVs$Mutation[index] <- paste(commonSNVs$annovar_chr[index], ":", commonSNVs$annovar_start[index], "-", commonSNVs$annovar_end[index] , " (", commonSNVs$gatk_mutation_type[index], ")<br/>func: ", commonSNVs$annovar_func[index], "<br/>ens func: ", commonSNVs$annovar_ens_func[index], sep = '')
index <- commonSNVs$aa != ""
commonSNVs$Mutation[index] <- paste(commonSNVs$annovar_chr[index], ":", commonSNVs$annovar_start[index], "-", commonSNVs$annovar_end[index] , " (", commonSNVs$gatk_mutation_type[index], ")<br/>func: ", commonSNVs$annovar_func[index], "<br/>ens func: ", commonSNVs$annovar_ens_func[index], "<br/>AA: ", commonSNVs$aa[index], sep = '')
commonSNVs$MuType <- "substitution"
printCommonSNVs <- commonSNVs[ which(commonSNVs$sampleID == moleID[1]), ]
otherCommonSNVs <- commonSNVs[ which(commonSNVs$sampleID != moleID[1]), ]
sidFreq <- data.frame(table(otherCommonSNVs$sampleID))
sidFreq <- sidFreq[order(-sidFreq$Freq), ]
names(sidFreq)[1] <- "sampleID"
sidFreq$sampleID <- as.character(sidFreq$sampleID)
numcol <- 10
if (length(sidFreq$sampleID) < 10) {
    numcol <- length(sidFreq$sampleID)
}
finalcolnames <- c('Gene', 'MutationType', 'Mutation', 'Interpretation', 'Cancer Gene', 'VAF')
for (i in 1:numcol) {
  index1 <- otherCommonSNVs$sampleID == sidFreq$sampleID[i]
  tempid <- paste("sampleID", i , sep = '')
  finalcolnames <- append(finalcolnames, tempid)
  printCommonSNVs[tempid] <- NA
  printCommonSNVs[[tempid]][printCommonSNVs$interID %in% otherCommonSNVs$interID[index1]] <- sidFreq$sampleID[i]
}
printCommonSNVs$sampleCount <- apply(printCommonSNVs[17:(16+numcol)], 1, function(x) sum(!is.na(x)))
finalcolnames <- append(finalcolnames, "sampleCount")
printCommonSNVs <- printCommonSNVs[c(1, 16, 15, 11,9,12, 17:(17+numcol))]
datatable(printCommonSNVs, escape = 2, rownames = FALSE,  colnames = finalcolnames)

Mutation Burden Plot

mutationBurden <- mysqlQuery("SELECT ids.sampleID, snv.count as snvH, snv.count/3.245446 AS MutationBurden FROM ( select sampleID,postprocID from sampleInfo AS si INNER JOIN variants_cancer AS vc ON si.postprocID = vc.tumor_name WHERE si.pairID >1 AND si.pairID < 9900 GROUP BY sampleID) ids LEFT JOIN (SELECT tumor_name, count(*) AS count FROM variants_cancer vc where ((t_ref_count + t_alt_count) >= 50 AND (n_ref_count + n_alt_count) >= 50 AND (annovar_complete_genomics != '' OR annovar_complete_genomics = 0 OR annovar_clinvar like '%CLIN%') AND ( annovar_1000g != '' OR annovar_1000g = 0 OR annovar_clinvar like '%CLIN%') AND judgement = 1) and length(in_cpanel) > 4 GROUP by tumor_name ) snv ON ids.postprocID = snv.tumor_name ORDER BY snvH DESC")
mutationBurden <- mutate(mutationBurden, curInv = sampleID != moleID[1] )
mutationBurden <- mutationBurden[ which(! is.na(mutationBurden$snvH)), ]
#mutationBurden %>% ggplot(aes(x=sampleID, y=MutationBurden, fill = curInv)) + geom_bar(stat = "identity", width=.7) + scale_x_discrete(limits = mutationBurden$sampleID) +  scale_y_log10( breaks = scales::trans_breaks("log10", function(x) 10^x), labels = scales::trans_format("log10", scales::math_format(10^.x)) ) + theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 6), legend.position="none", panel.grid.minor = element_blank())  + labs(y = "Mutation Burden")
mutationBurden %>% ggplot(aes(x=sampleID, y=MutationBurden*10, fill = curInv)) + geom_bar(stat = "identity", width=.7) + scale_x_discrete(limits = mutationBurden$sampleID) + scale_y_log10(breaks = c(1, 10, 100, 1000), labels = c("0", "1", "10", "100")) + theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 6), legend.position="none", panel.grid.minor = element_blank())  + labs(y = "Mutation Burden")

Overall Coverage

cmdQuery <- paste("SELECT perbasesAbove1XGP as '%>1X',perbasesAbove10XGP as '%>50X',perbasesAbove20XGP as '%>200X', perbasesAbove30XGP as '%>500X' FROM sampleInfo WHERE sampleID in ('", moleID[1], "')", collapse = "", sep = '')
cvgSum <- mysqlQuery(cmdQuery)
#barplot(c(cvgSum$`%>1X`[1], cvgSum$`%>50X`[1], cvgSum$`%>200X`[1], cvgSum$`%>500X`[1]), main="Overall Coverage", names.arg =c("%>1X", "%>50X", "%>200X", "%>500X"), ylab="Percentage of Bases", col = "cyan")
cvgBarPlot <- data.frame( items = names(cvgSum), values = c(cvgSum$`%>1X`[1], cvgSum$`%>50X`[1], cvgSum$`%>200X`[1], cvgSum$`%>500X`[1]))
cvgBarPlot$hline <- c(NA, 97, 95, 75)
itemorder <- c("%>1X",  "%>50X",  "%>200X", "%>500X")
cvgBarPlot %>% ggplot(aes(x=items, y=values)) + geom_bar(stat = "identity", width=.5, fill="steelblue") + scale_x_discrete(limits = itemorder) + geom_text(aes(label=values), vjust=-.5, color="black", size=3.5)  +  theme_minimal() + geom_errorbar(aes(width=.5, ymax=hline, ymin=hline), colour="#AA0000")

exon coverage plot

options(warn=-1)
cmdQuery <- paste("SELECT ec.chr,ec.start,ec.stop,ec.gene_exon,ec.coverage FROM exonCov AS ec INNER JOIN sampleInfo AS si ON si.postprocID = ec.postprocID WHERE si.sampleID ='", moleID[1], "'", collapse = "", sep = '')
covs <- mysqlQuery(cmdQuery)
blacklist <- mysqlQuery("SELECT lowCvgExon FROM lowCvgExon WHERE captureKit = 'cancer'")
blacklist <- unlist(strsplit(blacklist$lowCvgExon, "; "))
covs <- covs[ ! covs$gene_exon %in% blacklist, ]
cvgSum <- mysqlQuery(paste("SELECT meanCvgGP,perbasesAbove30XGP FROM sampleInfo WHERE sampleID = '", moleID[1], "'", sep = ''))
covs = mutate(covs, good_cov = coverage >= 50 )
covs = mutate(covs, bad_cov = coverage < 50)
covs$Gene <- sub("-.*", "", covs$gene_exon)
covs$Exon <- sub(".*-", "", covs$gene_exon)
covs$truec <- unsplit(lapply(split(covs, covs[c("Gene","bad_cov")]), nrow), covs[c("Gene")])
covs$falsec <- unsplit(lapply(split(covs, covs[c("Gene","good_cov")]), nrow), covs[c("Gene")])
covs$Gene_Cvg <- paste(covs$Gene, " (", covs$truec, "/", covs$falsec, ")", sep = '')
covs$Gene_Cvg <- factor(covs$Gene_Cvg, levels = covs$Gene_Cvg[order(covs$good_cov)])
covs %>% ggplot(aes(Exon,"crap",color  = good_cov))  + geom_point(shape = 3) + facet_wrap(  ~ Gene_Cvg, scales = "free") + ggtitle(paste('Coverage of all exons in the cancer panel for sample ', moleID[1], sep='')) + theme(plot.title = element_text(hjust = 0.5), plot.margin = unit(c(0.1, 0, 0, -0.8), "line"), panel.spacing = unit(0, "lines"), strip.text = element_text(size=5, hjust = 0), strip.background = element_rect(fill = "white", color = "white", size = 2)) + theme(axis.line.y = element_blank(), axis.ticks.y = element_blank(), strip.text.y = element_text(angle = 0)) + theme(axis.line.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x  = element_blank(), axis.text.y  = element_blank()) + xlab(paste("Average Coverage is ", cvgSum$meanCvgGP[1], "X, (w/ ", cvgSum$perbasesAbove30XGP[1], "% bases > 500X)\n", "Exons Coverage w/ ", nrow(covs[covs$good_cov == TRUE,]) , " > 50X, ", nrow(covs[covs$bad_cov == TRUE,]), " < 50X", sep = '')) 

Coverage distribution of Exons of all genes

cov_tumor <- mysqlQuery("SELECT gene_exon, coverage, pairID, CONCAT(gene_exon, '_', pairID) AS id_Uniq FROM exonCov INNER JOIN sampleInfo ON sampleInfo.postprocID = exonCov.postprocID where sampleType = 'tumor' AND pairID > 0 AND pairID < 9900 AND currentStatus > 7 AND coverage > 0")
cov_normal <- mysqlQuery("SELECT gene_exon, coverage, pairID, CONCAT(gene_exon, '_', pairID) AS id_Uniq_n FROM exonCov INNER JOIN sampleInfo ON sampleInfo.postprocID = exonCov.postprocID where sampleType = 'normal' AND pairID > 0 AND pairID < 9900 AND currentStatus > 7 and coverage > 0 GROUP BY pairID,gene_exon ORDER by sampleInfo.postprocID DESC")
blacklist <- mysqlQuery("SELECT lowCvgExon FROM lowCvgExon WHERE captureKit = 'cancer'")
blacklist <- unlist(strsplit(blacklist$lowCvgExon, "; "))
cov_tumor <- cov_tumor[ ! cov_tumor$gene_exon %in% blacklist, ]
cov_normal <- cov_normal[ ! cov_normal$gene_exon %in% blacklist, ]
cov4plot <- merge(cov_tumor, cov_normal, by.x='id_Uniq', by.y='id_Uniq_n')[, c(2,5,3,6,4)]
cov4plot$gene_exon.y <- sub("-.*", "", cov4plot$gene_exon.y)
cov4plot <- mutate(cov4plot, log_ratio = log2(cov4plot$coverage.x / cov4plot$coverage.y))
ggplot(cov4plot, aes(x=gene_exon.y, y=log_ratio)) + geom_boxplot()

Coverage distribution of Exons of certain gene

cov_mycn <- mysqlQuery("SELECT CONCAT(gene_exon, '\n', chr, ':', start, '-', stop) AS gene_exon, coverage, sampleID FROM exonCov INNER JOIN sampleInfo ON sampleInfo.postprocID = exonCov.postprocID where gene_exon like 'MYCN-%' AND sampleType = 'tumor' AND currentStatus > 7")
Closing open result sets
cov_mycn$hline <- cov_mycn[ which(cov_mycn$sampleID == moleID[1]),]$coverage
ggplot(cov_mycn, aes(x= gene_exon, y=coverage)) + geom_boxplot() +  scale_y_log10( breaks = scales::trans_breaks("log10", function(x) 10^x), labels = scales::trans_format("log10", scales::math_format(10^.x)) ) + geom_errorbar(aes(width=.5, ymax=hline, ymin=hline), colour="#AA0000")

Coverage distribution of Exons of certain gene

cov_gata1 <- mysqlQuery("SELECT CONCAT(gene_exon, '\n', chr, ':', start, '-', stop) AS gene_exon, coverage, sampleID FROM exonCov INNER JOIN sampleInfo ON sampleInfo.postprocID = exonCov.postprocID where gene_exon like 'GATA1-%' AND sampleType = 'tumor' AND currentStatus > 7")
Closing open result sets
cov_gata1$hline <- cov_gata1[ which(cov_gata1$sampleID == moleID[1]),]$coverage
ggplot(cov_gata1, aes(x= gene_exon, y=coverage)) + geom_boxplot() +  scale_y_log10( breaks = scales::trans_breaks("log10", function(x) 10^x), labels = scales::trans_format("log10", scales::math_format(10^.x)) )  + geom_errorbar(aes(width=.5, ymax=hline, ymin=hline), colour="#AA0000")

LS0tCnRpdGxlOiAiQ2FuY2VyIFBhbmVsIEludGVycHJldGF0aW9uIEFzc2lzdGFudCBLaXQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkhlYXRtYXAgb2YgUGFpcndpc2UgQ29tbW9uIFN1YnN0aXR1dGlvbnMgb2YgYWxsIFR1bW9yIFNhbXBsZXMgCgpgYGB7ciBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZyA9IEZBTFNFfQojbW9sZUlEIDwtIGMoJzI4ODU1NScpCm1vbGVJRCA8LSBjKCcyOTczNjgnKQoKbGlicmFyeShSTXlTUUwpCmxpYnJhcnkocmVwcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHBseXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoRFQpCgpteXNxbFF1ZXJ5IDwtIGZ1bmN0aW9uIChxdWVyeSkgewogIERCIDwtIGRiQ29ubmVjdChNeVNRTCgpLCB1c2VyPSdhc2hsaWVuJywgcGFzc3dvcmQ9J1JlNENuYSRjJywgZGJuYW1lPSdjbGluaWNhbEMnLCBob3N0PScxNzIuMjcuMjAuMjAnLCBwb3J0PTUwMjkpCiAgcnMgPC0gZGJTZW5kUXVlcnkoREIsIHF1ZXJ5KQogIHJlc3VsdCA8LSBmZXRjaChycywgLTEpCiAgZGJEaXNjb25uZWN0KERCKQogIHJldHVybihyZXN1bHQpCn0KCnNoYXJlU05WIDwtIG15c3FsUXVlcnkoIlNFTEVDVCBDT05DQVRfV1MoJ18nLCBzaS5zYW1wbGVJRCxzaS5wYWlySUQpIGFzIHNhbXBsZUlELHZjLmludGVySUQgRlJPTSB2YXJpYW50c19jYW5jZXIgQVMgdmMgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIEFTIHNpIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIGNyb3NzIGpvaW4gKHNlbGVjdCB2Yy5pbnRlcklELGNvdW50KCopIGZyb20gdmFyaWFudHNfY2FuY2VyIGFzIHZjIGlubmVyIGpvaW4gc2FtcGxlSW5mbyBhcyBzaSBvbiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSB3aGVyZSBzaS5wYWlySUQgPiAwIGFuZCBzaS5wYWlySUQgPCA5OTAwIEFORCAoKCh0X3JlZl9jb3VudCArIHRfYWx0X2NvdW50KSA+PSA1MCBBTkQgKG5fcmVmX2NvdW50ICsgbl9hbHRfY291bnQpID49IDUwIEFORCAoYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyAhPSAnJyBPUiBhbm5vdmFyX2NvbXBsZXRlX2dlbm9taWNzID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EICggYW5ub3Zhcl8xMDAwZyAhPSAnJyBPUiBhbm5vdmFyXzEwMDBnID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EIGp1ZGdlbWVudCA9IDEpIE9SICgoIGFubm92YXJfMTAwMGcgaXMgTlVMTCBPUiBhbm5vdmFyXzEwMDBnIDwgMC4wMSApIEFORCAoIGFubm92YXJfZXNwIGlzIE5VTEwgT1IgYW5ub3Zhcl9lc3AgPCAwLjAxKSBBTkQgdF9hbHRfY291bnQgPj0gMTAgQU5EICBuX2FsdF9jb3VudCA8IDMgQU5EIG5fcmVmX2NvdW50ID4gNTAgQU5EIHR1bW9yX2YgPiAwLjAxIEFORCBjb3NtaWNfY2Vuc3VzID0gMSBBTkQgKGFubm92YXJfZXhvbmljX2Z1bmMgUkVHRVhQICdub25zeW5vbnltb3VzIFNOVnxzdG9wZ2FpbiBTTlZ8c3RvcGxvc3MgU05WfHNwbGljaW5nJyBPUiAgYW5ub3Zhcl9mdW5jIFJFR0VYUCAnc3BsaWNpbmd8dXBzdHJlYW0nKSkpIGdyb3VwIGJ5IHZjLmludGVySUQgSEFWSU5HIGNvdW50KCopID4gMSkgdG1wIHdoZXJlIHRtcC5pbnRlcklEID0gdmMuaW50ZXJJRCBBTkQgc2kucGFpcklEID4gMCBhbmQgc2kucGFpcklEIDwgOTkwMCBhbmQgKCgodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgPj0gNTAgQU5EIChuX3JlZl9jb3VudCArIG5fYWx0X2NvdW50KSA+PSA1MCBBTkQgKGFubm92YXJfY29tcGxldGVfZ2Vub21pY3MgIT0gJycgT1IgYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCAoIGFubm92YXJfMTAwMGcgIT0gJycgT1IgYW5ub3Zhcl8xMDAwZyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCBqdWRnZW1lbnQgPSAxKSBPUiAoKCBhbm5vdmFyXzEwMDBnIGlzIE5VTEwgT1IgYW5ub3Zhcl8xMDAwZyA8IDAuMDEgKSBBTkQgKCBhbm5vdmFyX2VzcCBpcyBOVUxMIE9SIGFubm92YXJfZXNwIDwgMC4wMSkgQU5EIHRfYWx0X2NvdW50ID49IDEwIEFORCAgbl9hbHRfY291bnQgPCAzIEFORCBuX3JlZl9jb3VudCA+IDUwIEFORCB0dW1vcl9mID4gMC4wMSBBTkQgY29zbWljX2NlbnN1cyA9IDEgQU5EIChhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycgT1IgIGFubm92YXJfZnVuYyBSRUdFWFAgJ3NwbGljaW5nfHVwc3RyZWFtJykpKSIpCnNoYXJlU05WJHNhbXBsZUlEIDwtIHN1Yihtb2xlSURbMV0sIHBhc3RlKCc+PicsIG1vbGVJRFsxXSksIHNoYXJlU05WJHNhbXBsZUlEKQoKc2FtcGxlU05WIDwtIG15c3FsUXVlcnkoIlNFTEVDVCBDT05DQVRfV1MoJ18nLCBzaS5zYW1wbGVJRCxzaS5wYWlySUQpIGFzIHNhbXBsZUlELCBjb3VudCgqKSBhcyBjb3VudCBGUk9NIHZhcmlhbnRzX2NhbmNlciBBUyB2YyBJTk5FUiBKT0lOIHNhbXBsZUluZm8gQVMgc2kgT04gc2kucG9zdHByb2NJRCA9IHZjLnR1bW9yX25hbWUgV0hFUkUgc2kucGFpcklEID4gMCBBTkQgc2kucGFpcklEIDwgOTkwMCBBTkQgKCgodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgPj0gNTAgQU5EIChuX3JlZl9jb3VudCArIG5fYWx0X2NvdW50KSA+PSA1MCBBTkQgKGFubm92YXJfY29tcGxldGVfZ2Vub21pY3MgIT0gJycgT1IgYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCAoIGFubm92YXJfMTAwMGcgIT0gJycgT1IgYW5ub3Zhcl8xMDAwZyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCBqdWRnZW1lbnQgPSAxKSBPUiAoKCBhbm5vdmFyXzEwMDBnIGlzIE5VTEwgT1IgYW5ub3Zhcl8xMDAwZyA8IDAuMDEgKSBBTkQgKCBhbm5vdmFyX2VzcCBpcyBOVUxMIE9SIGFubm92YXJfZXNwIDwgMC4wMSkgQU5EIHRfYWx0X2NvdW50ID49IDEwIEFORCAgbl9hbHRfY291bnQgPCAzIEFORCBuX3JlZl9jb3VudCA+IDUwIEFORCB0dW1vcl9mID4gMC4wMSBBTkQgY29zbWljX2NlbnN1cyA9IDEgQU5EIChhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycgT1IgIGFubm92YXJfZnVuYyBSRUdFWFAgJ3NwbGljaW5nfHVwc3RyZWFtJykpKSBncm91cCBieSBzYW1wbGVJRCIpCnNhbXBsZVNOViRzYW1wbGVJRCA8LSBzdWIobW9sZUlEWzFdLCBwYXN0ZSgnPj4nLCBtb2xlSURbMV0pLCBzYW1wbGVTTlYkc2FtcGxlSUQpCgpoZWF0IDwtIG1hdHJpeCgwLCBsZW5ndGgodW5pcXVlKHNhbXBsZVNOViRzYW1wbGVJRCkpLCBsZW5ndGgodW5pcXVlKHNhbXBsZVNOViRzYW1wbGVJRCkpKQpjb2xuYW1lcyhoZWF0KSA8LSB1bmlxdWUoc2FtcGxlU05WJHNhbXBsZUlEKQpyb3duYW1lcyhoZWF0KSA8LSB1bmlxdWUoc2FtcGxlU05WJHNhbXBsZUlEKQoKZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZVNOViRzYW1wbGVJRCkpIHsKICBoZWF0W3NhbXBsZVNOViRzYW1wbGVJRFtpXSwgc2FtcGxlU05WJHNhbXBsZUlEW2ldXSA9IHNhbXBsZVNOViRjb3VudFtpXQp9Cgpmb3IgKCB2YWwxIGluIDE6KGxlbmd0aChzaGFyZVNOViRzYW1wbGVJRCktMSkgKSB7IGZvciAodmFsMiBpbiAodmFsMSsxKTpsZW5ndGgoc2hhcmVTTlYkc2FtcGxlSUQpKSB7IGlmIChzaGFyZVNOViRpbnRlcklEW3ZhbDFdID09IHNoYXJlU05WJGludGVySURbdmFsMl0pIHsgaGVhdFtzaGFyZVNOViRzYW1wbGVJRFt2YWwxXSwgIHNoYXJlU05WJHNhbXBsZUlEW3ZhbDJdXSA9IGhlYXRbc2hhcmVTTlYkc2FtcGxlSURbdmFsMV0sICBzaGFyZVNOViRzYW1wbGVJRFt2YWwyXV0gKyAxIDsgaGVhdFtzaGFyZVNOViRzYW1wbGVJRFt2YWwyXSwgIHNoYXJlU05WJHNhbXBsZUlEW3ZhbDFdXSA9IGhlYXRbc2hhcmVTTlYkc2FtcGxlSURbdmFsMl0sICBzaGFyZVNOViRzYW1wbGVJRFt2YWwxXV0gKyAxfSB9fQoKaGVhdC5tIDwtIG1lbHQoaGVhdCkKCmdncGxvdChoZWF0Lm0sIGFlcyhWYXIxLCBWYXIyLCBmaWxsID0gdmFsdWUpKSArIGdlb21fdGlsZShhZXMoZmlsbCA9IHZhbHVlKSkgKyAgZ2VvbV90ZXh0KGRhdGE9c3Vic2V0KGhlYXQubSwgdmFsdWUgPiAwKSwgYWVzKGxhYmVsID0gdmFsdWUpLCBzaXplID0gMS41KSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiKSArIHlsYWIoJ1NhbXBsZUlEX0tpQ1MnKSArIHhsYWIoIlNhbXBsZUlEX0tpQ1MiKSArIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT02KSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKCmBgYAoKCgpIZWF0bWFwIG9mIFBhaXJ3aXNlIENvbW1vbiBJTkRFTHMgb2YgYWxsIFR1bW9yIFNhbXBsZXMgCgpgYGB7ciBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNSwgd2FybmluZyA9IEZBTFNFfQpzaGFyZUlOREVMIDwtIG15c3FsUXVlcnkoIlNFTEVDVCBDT05DQVRfV1MoJ18nLCBzaS5zYW1wbGVJRCxzaS5wYWlySUQpIGFzIHNhbXBsZUlELHZjLmludGVySUQgRlJPTSBpbmRlbF9jYW5jZXIgQVMgdmMgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIEFTIHNpIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIGNyb3NzIGpvaW4gKHNlbGVjdCB2Yy5pbnRlcklELGNvdW50KCopIGZyb20gaW5kZWxfY2FuY2VyIGFzIHZjIGlubmVyIGpvaW4gc2FtcGxlSW5mbyBhcyBzaSBvbiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSB3aGVyZSBzaS5wYWlySUQgPiAwIGFuZCBzaS5wYWlySUQgPCA5OTAwIEFORCAoZ2F0a19tdXRhdGlvbl90eXBlICE9ICdzdWInIEFORCBnYXRrX25vcm1hbF9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfZGVwdGggPj0gMzAgQU5EIGdhdGtfdHVtb3VyX2FsbGVsZV9mcmFjdGlvbiA+PSAwLjA1IEFORCAoZ2F0a19maWx0ZXIgPSAnUEFTUycgT1IgKChnYXRrX2ZpbHRlciBOT1QgTElLRSAnY2x1c3RlcmVkX2V2ZW50cycgT1IgZ2F0a19maWx0ZXIgTk9UIExJS0UgJ2hvbW9sb2dvdXNfbWFwcGluZ19ldmVudCcpIEFORCBnYXRrX3R1bW91cl9hbHRfY291bnQgPj0gNSBBTkQgZ2F0a19ub3JtYWxfYWxsZWxlX2ZyYWN0aW9uIDwgMC4wMSkpKSBncm91cCBieSB2Yy5pbnRlcklEIEhBVklORyBjb3VudCgqKSA+IDEpIHRtcCB3aGVyZSB0bXAuaW50ZXJJRCA9IHZjLmludGVySUQgQU5EIHNpLnBhaXJJRCA+IDAgQU5EIHNpLnBhaXJJRCA8IDk5MDAgQU5EIChnYXRrX211dGF0aW9uX3R5cGUgIT0gJ3N1YicgQU5EIGdhdGtfbm9ybWFsX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfYWxsZWxlX2ZyYWN0aW9uID49IDAuMDUgQU5EIChnYXRrX2ZpbHRlciA9ICdQQVNTJyBPUiAoKGdhdGtfZmlsdGVyIE5PVCBMSUtFICdjbHVzdGVyZWRfZXZlbnRzJyBPUiBnYXRrX2ZpbHRlciBOT1QgTElLRSAnaG9tb2xvZ291c19tYXBwaW5nX2V2ZW50JykgQU5EIGdhdGtfdHVtb3VyX2FsdF9jb3VudCA+PSA1IEFORCBnYXRrX25vcm1hbF9hbGxlbGVfZnJhY3Rpb24gPCAwLjAxKSkpIikKc2hhcmVJTkRFTCRzYW1wbGVJRCA8LSBzdWIobW9sZUlEWzFdLCBwYXN0ZSgnPj4nLCBtb2xlSURbMV0pLCBzaGFyZUlOREVMJHNhbXBsZUlEKQoKc2FtcGxlSU5ERUwgPC0gbXlzcWxRdWVyeSgiU0VMRUNUIENPTkNBVF9XUygnXycsIHNpLnNhbXBsZUlELHNpLnBhaXJJRCkgYXMgc2FtcGxlSUQsIGNvdW50KCopIGFzIGNvdW50IEZST00gaW5kZWxfY2FuY2VyIEFTIHZjIElOTkVSIEpPSU4gc2FtcGxlSW5mbyBBUyBzaSBPTiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSBXSEVSRSBzaS5wYWlySUQgPiAwIEFORCBzaS5wYWlySUQgPCA5OTAwIEFORCAoZ2F0a19tdXRhdGlvbl90eXBlICE9ICdzdWInIEFORCBnYXRrX25vcm1hbF9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfZGVwdGggPj0gMzAgQU5EIGdhdGtfdHVtb3VyX2FsbGVsZV9mcmFjdGlvbiA+PSAwLjA1IEFORCAoZ2F0a19maWx0ZXIgPSAnUEFTUycgT1IgKChnYXRrX2ZpbHRlciBOT1QgTElLRSAnY2x1c3RlcmVkX2V2ZW50cycgT1IgZ2F0a19maWx0ZXIgTk9UIExJS0UgJ2hvbW9sb2dvdXNfbWFwcGluZ19ldmVudCcpIEFORCBnYXRrX3R1bW91cl9hbHRfY291bnQgPj0gNSBBTkQgZ2F0a19ub3JtYWxfYWxsZWxlX2ZyYWN0aW9uIDwgMC4wMSkpKSBncm91cCBieSBzYW1wbGVJRCIpCnNhbXBsZUlOREVMJHNhbXBsZUlEIDwtIHN1Yihtb2xlSURbMV0sIHBhc3RlKCc+PicsIG1vbGVJRFsxXSksIHNhbXBsZUlOREVMJHNhbXBsZUlEKQoKaGVhdCA8LSBtYXRyaXgoMCwgbGVuZ3RoKHVuaXF1ZShzYW1wbGVJTkRFTCRzYW1wbGVJRCkpLCBsZW5ndGgodW5pcXVlKHNhbXBsZUlOREVMJHNhbXBsZUlEKSkpCmNvbG5hbWVzKGhlYXQpIDwtIHVuaXF1ZShzYW1wbGVJTkRFTCRzYW1wbGVJRCkKcm93bmFtZXMoaGVhdCkgPC0gdW5pcXVlKHNhbXBsZUlOREVMJHNhbXBsZUlEKQoKZm9yIChpIGluIDE6bGVuZ3RoKHNhbXBsZUlOREVMJHNhbXBsZUlEKSkgewogIGhlYXRbc2FtcGxlSU5ERUwkc2FtcGxlSURbaV0sIHNhbXBsZUlOREVMJHNhbXBsZUlEW2ldXSA9IHNhbXBsZUlOREVMJGNvdW50W2ldCn0KCmZvciAoIHZhbDEgaW4gMToobGVuZ3RoKHNoYXJlSU5ERUwkc2FtcGxlSUQpLTEpICkgeyBmb3IgKHZhbDIgaW4gKHZhbDErMSk6bGVuZ3RoKHNoYXJlSU5ERUwkc2FtcGxlSUQpKSB7IGlmIChzaGFyZUlOREVMJGludGVySURbdmFsMV0gPT0gc2hhcmVJTkRFTCRpbnRlcklEW3ZhbDJdKSB7IGhlYXRbc2hhcmVJTkRFTCRzYW1wbGVJRFt2YWwxXSwgIHNoYXJlSU5ERUwkc2FtcGxlSURbdmFsMl1dID0gaGVhdFtzaGFyZUlOREVMJHNhbXBsZUlEW3ZhbDFdLCAgc2hhcmVJTkRFTCRzYW1wbGVJRFt2YWwyXV0gKyAxIDsgaGVhdFtzaGFyZUlOREVMJHNhbXBsZUlEW3ZhbDJdLCAgc2hhcmVJTkRFTCRzYW1wbGVJRFt2YWwxXV0gPSBoZWF0W3NoYXJlSU5ERUwkc2FtcGxlSURbdmFsMl0sICBzaGFyZUlOREVMJHNhbXBsZUlEW3ZhbDFdXSArIDF9IH19CgpoZWF0Lm0gPC0gbWVsdChoZWF0KQoKZ2dwbG90KGhlYXQubSwgYWVzKFZhcjEsIFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsgZ2VvbV90aWxlKGFlcyhmaWxsID0gdmFsdWUpKSArICBnZW9tX3RleHQoZGF0YT1zdWJzZXQoaGVhdC5tLCB2YWx1ZSA+IDApLCBhZXMobGFiZWwgPSB2YWx1ZSksIHNpemUgPSAxLjUpICsgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gInJlZCIpICsgeWxhYignU2FtcGxlSURfS2lDUycpICsgeGxhYigiU2FtcGxlSURfS2lDUyIpICsgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpgYGAKCgoKCk92ZXJhbGwgdmlldyBvZiBub25zeXNub255bW91cyBnZW5lcwpgYGB7cn0KYWxsX05vbnN5bm9uID0gbXlzcWxRdWVyeSgiU0VMRUNUICogZnJvbSAoU0VMRUNUIHNpLnNhbXBsZUlELCB2Yy5oZ25jX2dlbmUgRlJPTSBpbmRlbF9jYW5jZXIgQVMgdmMgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIEFTIHNpIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIFdIRVJFIHNpLnBhaXJJRCA+IDAgQU5EIHNpLnBhaXJJRCA8IDk5MDAgQU5EIChnYXRrX211dGF0aW9uX3R5cGUgIT0gJ3N1YicgQU5EIGdhdGtfbm9ybWFsX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfYWxsZWxlX2ZyYWN0aW9uID49IDAuMDUgQU5EIChnYXRrX2ZpbHRlciA9ICdQQVNTJyBPUiAoKGdhdGtfZmlsdGVyIE5PVCBMSUtFICdjbHVzdGVyZWRfZXZlbnRzJyBPUiBnYXRrX2ZpbHRlciBOT1QgTElLRSAnaG9tb2xvZ291c19tYXBwaW5nX2V2ZW50JykgQU5EIGdhdGtfdHVtb3VyX2FsdF9jb3VudCA+PSA1IEFORCBnYXRrX25vcm1hbF9hbGxlbGVfZnJhY3Rpb24gPCAwLjAxKSkpIEFORCBhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnZnJhbWVzaGlmdCBkZWxldGlvbnxmcmFtZXNoaWZ0IGluc2VydGlvbnxub25mcmFtZXNoaWZ0IGRlbGV0aW9ufG5vbmZyYW1lc2hpZnQgaW5zZXJ0aW9ufHN0b3BnYWluIFNOVnxzdG9wbG9zcyBTTlYnIFVOSU9OIFNFTEVDVCBzaS5zYW1wbGVJRCwgdmMuaGduY19nZW5lIEZST00gdmFyaWFudHNfY2FuY2VyIEFTIHZjIElOTkVSIEpPSU4gc2FtcGxlSW5mbyBBUyBzaSBPTiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSBXSEVSRSBzaS5wYWlySUQgPiAwIEFORCBzaS5wYWlySUQgPCA5OTAwIEFORCAoKCh0X3JlZl9jb3VudCArIHRfYWx0X2NvdW50KSA+PSA1MCBBTkQgKG5fcmVmX2NvdW50ICsgbl9hbHRfY291bnQpID49IDUwIEFORCAoYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyAhPSAnJyBPUiBhbm5vdmFyX2NvbXBsZXRlX2dlbm9taWNzID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EICggYW5ub3Zhcl8xMDAwZyAhPSAnJyBPUiBhbm5vdmFyXzEwMDBnID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EIGp1ZGdlbWVudCA9IDEpIE9SICgoIGFubm92YXJfMTAwMGcgaXMgTlVMTCBPUiBhbm5vdmFyXzEwMDBnIDwgMC4wMSApIEFORCAoIGFubm92YXJfZXNwIGlzIE5VTEwgT1IgYW5ub3Zhcl9lc3AgPCAwLjAxKSBBTkQgdF9hbHRfY291bnQgPj0gMTAgQU5EICBuX2FsdF9jb3VudCA8IDMgQU5EIG5fcmVmX2NvdW50ID4gNTAgQU5EIHR1bW9yX2YgPiAwLjAxIEFORCBjb3NtaWNfY2Vuc3VzID0gMSBBTkQgKGFubm92YXJfZXhvbmljX2Z1bmMgUkVHRVhQICdub25zeW5vbnltb3VzIFNOVnxzdG9wZ2FpbiBTTlZ8c3RvcGxvc3MgU05WfHNwbGljaW5nJyBPUiAgYW5ub3Zhcl9mdW5jIFJFR0VYUCAnc3BsaWNpbmd8dXBzdHJlYW0nKSkpIEFORCBhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycpIHRtcCBHUk9VUCBieSBzYW1wbGVJRCxoZ25jX2dlbmUiKQoKYWxsR2VuZXMgPC0gbWF0cml4KE5BLCBsZW5ndGgodW5pcXVlKGFsbF9Ob25zeW5vbiRoZ25jX2dlbmUpKSwgbGVuZ3RoKHVuaXF1ZShhbGxfTm9uc3lub24kc2FtcGxlSUQpKSkKY29sbmFtZXMoYWxsR2VuZXMpIDwtIHVuaXF1ZShhbGxfTm9uc3lub24kc2FtcGxlSUQpCnJvd25hbWVzKGFsbEdlbmVzKSA8LSB1bmlxdWUoYWxsX05vbnN5bm9uJGhnbmNfZ2VuZSkKZm9yIChpIGluIDE6bGVuZ3RoKGFsbF9Ob25zeW5vbiRzYW1wbGVJRCkpIHsKICBhbGxHZW5lc1thbGxfTm9uc3lub24kaGduY19nZW5lW2ldLCBhbGxfTm9uc3lub24kc2FtcGxlSURbaV1dIDwtIFRSVUUKfQoKb3JkZXJfbmFtZXMgPC0gdW5pcXVlKGFsbF9Ob25zeW5vbiRzYW1wbGVJRCkKb3JkZXJfbmFtZXMgPC0gb3JkZXJfbmFtZXNbICEgb3JkZXJfbmFtZXMgJWluJSBtb2xlSUQgXQpvcmRlcl9uYW1lcyA8LSBjKG1vbGVJRFsxXSwgb3JkZXJfbmFtZXMpCmFsbEdlbmVzIDwtIGFsbEdlbmVzWyxvcmRlcl9uYW1lc10KCmFsbEdlbmVzIDwtIGNiaW5kKGFsbEdlbmVzLCBDb3VudCA9IGFwcGx5KGFsbEdlbmVzLCAxLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkpCmFsbEdlbmVzIDwtIGFsbEdlbmVzWyB3aGljaChhbGxHZW5lc1ssICdDb3VudCddID4gIjEiKSwgXQphbGxHZW5lcyA8LSBhbGxHZW5lc1sgb3JkZXIoLWFsbEdlbmVzWyxuY29sKGFsbEdlbmVzKV0pLF0KCmRhdGF0YWJsZShhbGxHZW5lcykgJT4lIGZvcm1hdFN0eWxlKG1vbGVJRCwgIGNvbG9yID0gJ3JlZCcsIGJhY2tncm91bmRDb2xvciA9ICdvcmFuZ2UnLCBmb250V2VpZ2h0ID0gJ2JvbGQnKQoKYGBgCgpgYGB7ciBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gMTYsIHdhcm5pbmcgPSBGQUxTRX0KYWxsR2VuZXMgPC0gbWVsdChhbGxHZW5lcykKYWxsR2VuZXMgPC0gYWxsR2VuZXNbIHdoaWNoKGFsbEdlbmVzJHZhbHVlID09IDEpLCBdCmFsbEdlbmVzWyB3aGljaChhbGxHZW5lcyRWYXIyID09IG1vbGVJRFsxXSksXSR2YWx1ZSA8LSAyCmFsbEdlbmVzJHZhbHVlIDwtIGZhY3RvcihhbGxHZW5lcyR2YWx1ZSwgbGV2ZWxzID0gYWxsR2VuZXMkdmFsdWUpIAphbGxHZW5lcyAlPiUgZ2dwbG90KGFlcyhWYXIyLCBWYXIxKSkgICsgZ2VvbV9wb2ludChzaGFwZSA9IDQsIGFlcyhjb2xvcj12YWx1ZSkpICsgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCiMrIGZhY2V0X3dyYXAoICB+IEdlbmVfQ3ZnLCBzY2FsZXMgPSAiZnJlZSIpICsgZ2d0aXRsZShwYXN0ZSgnQ292ZXJhZ2Ugb2YgYWxsIGV4b25zIGluIHRoZSBjYW5jZXIgcGFuZWwgZm9yIHNhbXBsZSAnLCBtb2xlSURbMV0sIHNlcD0nJykpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIHBsb3QubWFyZ2luID0gdW5pdChjKDAuMSwgMCwgMCwgLTAuOCksICJsaW5lIiksIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAsICJsaW5lcyIpLCBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9NSwgaGp1c3QgPSAwKSwgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMikpICsgdGhlbWUoYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCkpICsgdGhlbWUoYXhpcy5saW5lLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSAgPSBlbGVtZW50X2JsYW5rKCkpICsgeGxhYihwYXN0ZSgiQXZlcmFnZSBDb3ZlcmFnZSBpcyAiLCBjdmdTdW0kbWVhbkN2Z0dQWzFdLCAiWCwgKHcvICIsIGN2Z1N1bSRwZXJiYXNlc0Fib3ZlMzBYR1BbMV0sICIlIGJhc2VzID4gNTAwWClcbiIsICJFeG9ucyBDb3ZlcmFnZSB3LyAiLCBucm93KGNvdnNbY292cyRnb29kX2NvdiA9PSBUUlVFLF0pICwgIiA+IDUwWCwgIiwgbnJvdyhjb3ZzW2NvdnMkYmFkX2NvdiA9PSBUUlVFLF0pLCAiIDwgNTBYIiwgc2VwID0gJycpKQpgYGAKCgpDdXJyZW50IEludmVzdGlnYXRlZCBzYW1wbGUgYW5kIHRoZSBub25zeW5vbnltb3VzIE11dGF0aW9ucyBvbiB0aGUgc2FtZSBnZW5lIAoKYGBge3IgIHdhcm5pbmcgPSBGQUxTRX0Kcm93cyA9IGZ1bmN0aW9uKHRhYikgbGFwcGx5KAogIHNlcV9sZW4obnJvdyh0YWIpKSwKICBmdW5jdGlvbihpKSB1bmNsYXNzKHRhYltpLCxkcm9wPUZdKQopCgpjbWRRdWVyeSA8LSBwYXN0ZSgiU0VMRUNUIHZjLmhnbmNfZ2VuZSwgdmMuYW5ub3Zhcl9jaHIsIHZjLmFubm92YXJfc3RhcnQsIHZjLmFubm92YXJfZW5kLCB2Yy5tdXRhdGlvblR5cGUsIHZjLmFubm92YXJfZnVuYywgdmMuYW5ub3Zhcl9lbnNfZnVuYywgdmMuYWEsIHZjLmNvc21pY19jZW5zdXMsIHNpLnNhbXBsZUlELCBpcC5pbnRlcnByZXRhdGlvbiwgdmMudHVtb3JfZiwgIENPTkNBVF9XUygnXycsIHZjLmludGVySUQsIHZjLnJlZl9hbGxlbGUsIHZjLmFsdF9hbGxlbGUpIGFzIG5ld2ludGVySUQgRlJPTSB2YXJpYW50c19jYW5jZXIgQVMgdmMgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIEFTIHNpIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIExFRlQgSk9JTiBpbnRlcnByZXRhdGlvbl9jYW5jZXIgYXMgaXAgb24gaXAucG9zdHByb2NJRCA9IHZjLnR1bW9yX25hbWUgQU5EIGlwLmludGVySUQgPSB2Yy5pbnRlcklEIENST1NTIEpPSU4gKCBTRUxFQ1QgdmMuZW5zZW1ibF9nZW5lIEZST00gdmFyaWFudHNfY2FuY2VyIGFzIHZjIElOTkVSIEpPSU4gc2FtcGxlSW5mbyBBUyBzaSBPTiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSBXSEVSRSBzaS5zYW1wbGVJRCA9ICciLCBtb2xlSURbMV0sICInIEFORCAoKCh0X3JlZl9jb3VudCArIHRfYWx0X2NvdW50KSA+PSA1MCBBTkQgKG5fcmVmX2NvdW50ICsgbl9hbHRfY291bnQpID49IDUwIEFORCAoYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyAhPSAnJyBPUiBhbm5vdmFyX2NvbXBsZXRlX2dlbm9taWNzID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EICggYW5ub3Zhcl8xMDAwZyAhPSAnJyBPUiBhbm5vdmFyXzEwMDBnID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EIGp1ZGdlbWVudCA9IDEpIE9SICgoIGFubm92YXJfMTAwMGcgaXMgTlVMTCBPUiBhbm5vdmFyXzEwMDBnIDwgMC4wMSApIEFORCAoIGFubm92YXJfZXNwIGlzIE5VTEwgT1IgYW5ub3Zhcl9lc3AgPCAwLjAxKSBBTkQgdF9hbHRfY291bnQgPj0gMTAgQU5EICBuX2FsdF9jb3VudCA8IDMgQU5EIG5fcmVmX2NvdW50ID4gNTAgQU5EIHR1bW9yX2YgPiAwLjAxIEFORCBjb3NtaWNfY2Vuc3VzID0gMSBBTkQgKGFubm92YXJfZXhvbmljX2Z1bmMgUkVHRVhQICdub25zeW5vbnltb3VzIFNOVnxzdG9wZ2FpbiBTTlZ8c3RvcGxvc3MgU05WfHNwbGljaW5nJyBPUiAgYW5ub3Zhcl9mdW5jIFJFR0VYUCAnc3BsaWNpbmd8dXBzdHJlYW0nKSkpKSBBUyB0bXAgV0hFUkUgKCgodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgPj0gNTAgQU5EIChuX3JlZl9jb3VudCArIG5fYWx0X2NvdW50KSA+PSA1MCBBTkQgKGFubm92YXJfY29tcGxldGVfZ2Vub21pY3MgIT0gJycgT1IgYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCAoIGFubm92YXJfMTAwMGcgIT0gJycgT1IgYW5ub3Zhcl8xMDAwZyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCBqdWRnZW1lbnQgPSAxKSBPUiAoKCBhbm5vdmFyXzEwMDBnIGlzIE5VTEwgT1IgYW5ub3Zhcl8xMDAwZyA8IDAuMDEgKSBBTkQgKCBhbm5vdmFyX2VzcCBpcyBOVUxMIE9SIGFubm92YXJfZXNwIDwgMC4wMSkgQU5EIHRfYWx0X2NvdW50ID49IDEwIEFORCAgbl9hbHRfY291bnQgPCAzIEFORCBuX3JlZl9jb3VudCA+IDUwIEFORCB0dW1vcl9mID4gMC4wMSBBTkQgY29zbWljX2NlbnN1cyA9IDEgQU5EIChhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycgT1IgIGFubm92YXJfZnVuYyBSRUdFWFAgJ3NwbGljaW5nfHVwc3RyZWFtJykpKSBBTkQgc2kucGFpcklEID4gMCBBTkQgc2kucGFpcklEIDwgOTkwMCBBTkQgKCh2Yy5lbnNlbWJsX2dlbmUgPSB0bXAuZW5zZW1ibF9nZW5lIEFORCB2Yy5hbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycpIE9SIHNpLnNhbXBsZUlEID0gJyIsIG1vbGVJRFsxXSwgIicpIEdST1VQIGJ5IHZjLmludGVySUQsdHVtb3JfbmFtZSIsIGNvbGxhcHNlID0gIiIsIHNlcCA9ICcnKQpjdXJJbnZlc3RTTlYgPC0gbXlzcWxRdWVyeShjbWRRdWVyeSkKaW50ZXJUcmFucyA8LSBjKCI3IiA9ICJVbmtub3duIiwgIjYiID0gIkNsYXNzLUUiLCAiNSIgPSAiQ2xhc3MtRCIsICI0IiA9ICJDbGFzcy1DIiwgIjMiID0gIkNsYXNzLUIiLCAiMiIgPSAiQ2xhc3MtQSIsICIxIiA9ICJVbmtub3duIiwgIjAiID0gIlVua25vd24iKQoKCmN1ckludmVzdFNOViRpbnRlcnByZXRhdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY3VySW52ZXN0U05WJGludGVycHJldGF0aW9uKQpjdXJJbnZlc3RTTlYkaW50ZXJwcmV0YXRpb24gPC0gcmV2YWx1ZShjdXJJbnZlc3RTTlYkaW50ZXJwcmV0YXRpb24sIGludGVyVHJhbnMpCmN1ckludmVzdFNOViRjb3NtaWNfY2Vuc3VzW2N1ckludmVzdFNOViRjb3NtaWNfY2Vuc3VzID09IDFdIDwtICJUIgpjdXJJbnZlc3RTTlYkY29zbWljX2NlbnN1c1tjdXJJbnZlc3RTTlYkY29zbWljX2NlbnN1cyA9PSAwXSA8LSAiRiIKY3VySW52ZXN0U05WJE11dGF0aW9uIDwtIE5VTEwKaW5kZXggPC0gY3VySW52ZXN0U05WJGFhID09ICIiCmN1ckludmVzdFNOViRNdXRhdGlvbltpbmRleF0gPC0gcGFzdGUoY3VySW52ZXN0U05WJGFubm92YXJfY2hyW2luZGV4XSwgIjoiLCBjdXJJbnZlc3RTTlYkYW5ub3Zhcl9zdGFydFtpbmRleF0sICItIiwgY3VySW52ZXN0U05WJGFubm92YXJfZW5kW2luZGV4XSAsICIgKCIsIGN1ckludmVzdFNOViRtdXRhdGlvblR5cGVbaW5kZXhdLCAiKTxici8+ZnVuYzogIiwgY3VySW52ZXN0U05WJGFubm92YXJfZnVuY1tpbmRleF0sICI8YnIvPmVucyBmdW5jOiAiLCBjdXJJbnZlc3RTTlYkYW5ub3Zhcl9lbnNfZnVuY1tpbmRleF0sIHNlcCA9ICcnKQppbmRleCA8LSBjdXJJbnZlc3RTTlYkYWEgIT0gIiIKY3VySW52ZXN0U05WJE11dGF0aW9uW2luZGV4XSA8LSBwYXN0ZShjdXJJbnZlc3RTTlYkYW5ub3Zhcl9jaHJbaW5kZXhdLCAiOiIsIGN1ckludmVzdFNOViRhbm5vdmFyX3N0YXJ0W2luZGV4XSwgIi0iLCBjdXJJbnZlc3RTTlYkYW5ub3Zhcl9lbmRbaW5kZXhdICwgIiAoIiwgY3VySW52ZXN0U05WJG11dGF0aW9uVHlwZVtpbmRleF0sICIpPGJyLz5mdW5jOiAiLCBjdXJJbnZlc3RTTlYkYW5ub3Zhcl9mdW5jW2luZGV4XSwgIjxici8+ZW5zIGZ1bmM6ICIsIGN1ckludmVzdFNOViRhbm5vdmFyX2Vuc19mdW5jW2luZGV4XSwgIjxici8+QUE6ICIsIGN1ckludmVzdFNOViRhYVtpbmRleF0sIHNlcCA9ICcnKQpjdXJJbnZlc3RTTlYkTXVUeXBlIDwtICJzdWJzdGl0dXRpb24iCm90aGVyU05WIDwtIGN1ckludmVzdFNOVlsgd2hpY2goY3VySW52ZXN0U05WJHNhbXBsZUlEICE9IG1vbGVJRFsxXSksIF0Kb3RoZXJTTlYgPC0gb3RoZXJTTlZbb3JkZXIob3RoZXJTTlYkc2FtcGxlSUQpLCBdCmN1cnJlbnRTTlYgPSBjdXJJbnZlc3RTTlZbIHdoaWNoKGN1ckludmVzdFNOViRzYW1wbGVJRCA9PSBtb2xlSURbMV0pLCBdCnNudlByaW50IDwtIGN1cnJlbnRTTlZbYygxLCAxNSwgMTQsIDExLDksMTIpXQpzbnZQcmludCRvdGhlciA8LSAiIgpmb3IgKGkgaW4gcm93cyhvdGhlclNOVikpIHsKICBzbnZQcmludCRvdGhlclsgc252UHJpbnQkaGduY19nZW5lID09IGkkaGduY19nZW5lIF0gPC0gcGFzdGUoaSRhYSwgIigiLCBpJHNhbXBsZUlELCAiKSwgIiwgc252UHJpbnQkb3RoZXJbIHNudlByaW50JGhnbmNfZ2VuZSA9PSBpJGhnbmNfZ2VuZSBdLCBzZXAgPSAiIikKfQoKY21kUXVlcnkgPC0gcGFzdGUoIlNFTEVDVCB2Yy5oZ25jX2dlbmUsIHZjLmFubm92YXJfY2hyLCB2Yy5hbm5vdmFyX3N0YXJ0LCB2Yy5hbm5vdmFyX2VuZCwgdmMuYW5ub3Zhcl9yZWYsIHZjLmFubm92YXJfYWx0LCB2Yy5nYXRrX211dGF0aW9uX3R5cGUgYXMgbXV0YXRpb25UeXBlLCB2Yy5hbm5vdmFyX2Z1bmMsIHZjLmFubm92YXJfZW5zX2Z1bmMsIHZjLmFhLCB2Yy5jb3NtaWNfY2Vuc3VzLCBzaS5zYW1wbGVJRCwgaXAuaW50ZXJwcmV0YXRpb24sIHZjLmdhdGtfdHVtb3VyX2FsbGVsZV9mcmFjdGlvbiBGUk9NIGluZGVsX2NhbmNlciBBUyB2YyBJTk5FUiBKT0lOIHNhbXBsZUluZm8gQVMgc2kgT04gc2kucG9zdHByb2NJRCA9IHZjLnR1bW9yX25hbWUgTEVGVCBKT0lOIGludGVycHJldGF0aW9uX2NhbmNlciBhcyBpcCBvbiBpcC5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSBBTkQgaXAuaW50ZXJJRCA9IHZjLmludGVySUQgQ1JPU1MgSk9JTiAoIFNFTEVDVCB2Yy5lbnNlbWJsX2dlbmUgRlJPTSBpbmRlbF9jYW5jZXIgYXMgdmMgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIEFTIHNpIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIFdIRVJFIHNpLnNhbXBsZUlEID0gJyIsIG1vbGVJRFsxXSAsIicgQU5EIChnYXRrX211dGF0aW9uX3R5cGUgIT0gJ3N1YicgQU5EIGdhdGtfbm9ybWFsX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfYWxsZWxlX2ZyYWN0aW9uID49IDAuMDUgQU5EIChnYXRrX2ZpbHRlciA9ICdQQVNTJyBPUiAoKGdhdGtfZmlsdGVyIE5PVCBMSUtFICdjbHVzdGVyZWRfZXZlbnRzJyBPUiBnYXRrX2ZpbHRlciBOT1QgTElLRSAnaG9tb2xvZ291c19tYXBwaW5nX2V2ZW50JykgQU5EIGdhdGtfdHVtb3VyX2FsdF9jb3VudCA+PSA1IEFORCBnYXRrX25vcm1hbF9hbGxlbGVfZnJhY3Rpb24gPCAwLjAxKSkpKSBBUyB0bXAgV0hFUkUgKGdhdGtfbXV0YXRpb25fdHlwZSAhPSAnc3ViJyBBTkQgZ2F0a19ub3JtYWxfZGVwdGggPj0gMzAgQU5EIGdhdGtfdHVtb3VyX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9hbGxlbGVfZnJhY3Rpb24gPj0gMC4wNSBBTkQgKGdhdGtfZmlsdGVyID0gJ1BBU1MnIE9SICgoZ2F0a19maWx0ZXIgTk9UIExJS0UgJ2NsdXN0ZXJlZF9ldmVudHMnIE9SIGdhdGtfZmlsdGVyIE5PVCBMSUtFICdob21vbG9nb3VzX21hcHBpbmdfZXZlbnQnKSBBTkQgZ2F0a190dW1vdXJfYWx0X2NvdW50ID49IDUgQU5EIGdhdGtfbm9ybWFsX2FsbGVsZV9mcmFjdGlvbiA8IDAuMDEpKSkgQU5EIHNpLnBhaXJJRCA+IDAgQU5EIHNpLnBhaXJJRCA8IDk5MDAgQU5EICgodmMuZW5zZW1ibF9nZW5lID0gdG1wLmVuc2VtYmxfZ2VuZSBBTkQgdmMuYW5ub3Zhcl9leG9uaWNfZnVuYyBSRUdFWFAgJ25vbnN5bm9ueW1vdXMgU05WfHN0b3BnYWluIFNOVnxzdG9wbG9zcyBTTlZ8c3BsaWNpbmcnKSBPUiBzaS5zYW1wbGVJRCA9ICciLCBtb2xlSURbMV0gLCInKSBHUk9VUCBieSB2Yy5pbnRlcklELHR1bW9yX25hbWUiLCBjb2xsYXBzZSA9ICIiLCBzZXAgPSAnJykKY3VySW52ZXN0SU5ERUwgPC0gbXlzcWxRdWVyeShjbWRRdWVyeSkKY3VySW52ZXN0SU5ERUwkaW50ZXJwcmV0YXRpb24gPC0gYXMuY2hhcmFjdGVyKGN1ckludmVzdElOREVMJGludGVycHJldGF0aW9uKQpjdXJJbnZlc3RJTkRFTCRpbnRlcnByZXRhdGlvbiA8LSByZXZhbHVlKGN1ckludmVzdElOREVMJGludGVycHJldGF0aW9uLCBpbnRlclRyYW5zKQpjdXJJbnZlc3RJTkRFTCRjb3NtaWNfY2Vuc3VzW2N1ckludmVzdElOREVMJGNvc21pY19jZW5zdXMgPT0gMV0gPC0gIlQiCmN1ckludmVzdElOREVMJGNvc21pY19jZW5zdXNbY3VySW52ZXN0SU5ERUwkY29zbWljX2NlbnN1cyA9PSAwXSA8LSAiRiIKY3VySW52ZXN0SU5ERUwkTXV0YXRpb24gPC0gTlVMTAppbmRleCA8LSBjdXJJbnZlc3RJTkRFTCRhYSA9PSAiIgpjdXJJbnZlc3RJTkRFTCRNdXRhdGlvbltpbmRleF0gPC0gcGFzdGUoY3VySW52ZXN0SU5ERUwkYW5ub3Zhcl9jaHJbaW5kZXhdLCAiOiIsIGN1ckludmVzdElOREVMJGFubm92YXJfc3RhcnRbaW5kZXhdLCAiLSIsIGN1ckludmVzdElOREVMJGFubm92YXJfZW5kW2luZGV4XSAsICIgKCAiLCBjdXJJbnZlc3RJTkRFTCRhbm5vdmFyX3JlZiwgIiA+ICIsIGN1ckludmVzdElOREVMJGFubm92YXJfYWx0LCAiICk8YnIvPmZ1bmM6ICIsIGN1ckludmVzdElOREVMJGFubm92YXJfZnVuY1tpbmRleF0sICI8YnIvPmVucyBmdW5jOiAiLCBjdXJJbnZlc3RJTkRFTCRhbm5vdmFyX2Vuc19mdW5jW2luZGV4XSwgc2VwID0gJycpCmluZGV4IDwtIGN1ckludmVzdElOREVMJGFhICE9ICIiCmN1ckludmVzdElOREVMJE11dGF0aW9uW2luZGV4XSA8LSBwYXN0ZShjdXJJbnZlc3RJTkRFTCRhbm5vdmFyX2NocltpbmRleF0sICI6IiwgY3VySW52ZXN0SU5ERUwkYW5ub3Zhcl9zdGFydFtpbmRleF0sICItIiwgY3VySW52ZXN0SU5ERUwkYW5ub3Zhcl9lbmRbaW5kZXhdICwgIiAoICIsIGN1ckludmVzdElOREVMJGFubm92YXJfcmVmLCAiID4gIiwgY3VySW52ZXN0SU5ERUwkYW5ub3Zhcl9hbHQsICIgKTxici8+ZnVuYzogIiwgY3VySW52ZXN0SU5ERUwkYW5ub3Zhcl9mdW5jW2luZGV4XSwgIjxici8+ZW5zIGZ1bmM6ICIsIGN1ckludmVzdElOREVMJGFubm92YXJfZW5zX2Z1bmNbaW5kZXhdLCAiPGJyLz5BQTogIiwgY3VySW52ZXN0SU5ERUwkYWFbaW5kZXhdLCBzZXAgPSAnJykKaW5kZWxUcmFucyA8LSBjKCJpbnMiID0gImluc2VydGlvbiIsICJkZWwiID0gImRlbGV0aW9uIikKY3VySW52ZXN0SU5ERUwkbXV0YXRpb25UeXBlIDwtIHJldmFsdWUoY3VySW52ZXN0SU5ERUwkbXV0YXRpb25UeXBlLCBpbmRlbFRyYW5zKQpvdGhlcklOREVMID0gY3VySW52ZXN0SU5ERUxbIHdoaWNoKCBjdXJJbnZlc3RJTkRFTCRzYW1wbGVJRCAhPSBtb2xlSURbMV0pLCBdCmN1cnJlbnRJTkRFTCA9IGN1ckludmVzdFNOVlsgd2hpY2goY3VySW52ZXN0U05WJHNhbXBsZUlEID09IG1vbGVJRFsxXSksIF0KaW5kZWxQcmludCA8LSBjdXJJbnZlc3RJTkRFTFtjKDEsNywxNSwxMywxMSwxNCldCmluZGVsUHJpbnQkb3RoZXIgPC0gIiIKZm9yIChpIGluIHJvd3Mob3RoZXJJTkRFTCkpIHsKICBpbmRlbFByaW50JG90aGVyWyBpbmRlbFByaW50JGhnbmNfZ2VuZSA9PSBpJGhnbmNfZ2VuZSBdIDwtIHBhc3RlKGkkYWEsICIoIiwgaSRzYW1wbGVJRCwgIiksICIsIGluZGVsUHJpbnQkb3RoZXJbIGluZGVsUHJpbnQkaGduY19nZW5lID09IGkkaGduY19nZW5lIF0sIHNlcCA9ICIiKQp9CgpwcmludFRhYmxlIDwtIGFzLmRhdGEuZnJhbWUobWFwcGx5KGMsIHNudlByaW50LCBpbmRlbFByaW50ICkpCgpkYXRhdGFibGUocHJpbnRUYWJsZSwgZXNjYXBlID0gMiwgcm93bmFtZXMgPSBGQUxTRSwgIGNvbG5hbWVzID0gYygnR2VuZScsICdNdXRhdGlvblR5cGUnLCAnTXV0YXRpb24nLCAnSW50ZXJwcmV0YXRpb24nLCAnQ2FuY2VyIEdlbmUnLCAnVkFGJywgJ011dGF0aW9uIG9uIE90aGVycycpKQpgYGAKCgpDb21tb24gc3Vic3RpdHV0aW9ucyBiZXR3ZWVuIGN1cnJlbnQgaW52ZXN0aWdhdGVkIHNhbXBsZSBhbmQgb3RoZXIgc2FtcGxlczoKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KY21kUXVlcnkgPC0gcGFzdGUoIlNFTEVDVCB2Yy5oZ25jX2dlbmUsIHZjLmFubm92YXJfY2hyLCB2Yy5hbm5vdmFyX3N0YXJ0LCB2Yy5hbm5vdmFyX2VuZCwgdmMubXV0YXRpb25UeXBlLCB2Yy5hbm5vdmFyX2Z1bmMsIHZjLmFubm92YXJfZW5zX2Z1bmMsIHZjLmFhLCB2Yy5jb3NtaWNfY2Vuc3VzLCBzaS5zYW1wbGVJRCwgaXAuaW50ZXJwcmV0YXRpb24sIHZjLnR1bW9yX2YsIHZjLmludGVySUQsIENPTkNBVF9XUygnXycsIHZjLmludGVySUQsIHZjLnJlZl9hbGxlbGUsIHZjLmFsdF9hbGxlbGUpIGFzIG5ld2ludGVySUQgRlJPTSB2YXJpYW50c19jYW5jZXIgQVMgdmMgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIEFTIHNpIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIExFRlQgSk9JTiBpbnRlcnByZXRhdGlvbl9jYW5jZXIgYXMgaXAgb24gaXAucG9zdHByb2NJRCA9IHZjLnR1bW9yX25hbWUgQU5EIGlwLmludGVySUQgPSB2Yy5pbnRlcklEIGNyb3NzIGpvaW4gKHNlbGVjdCB2Yy5pbnRlcklELGNvdW50KCopIGZyb20gdmFyaWFudHNfY2FuY2VyIGFzIHZjIGlubmVyIGpvaW4gc2FtcGxlSW5mbyBhcyBzaSBvbiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSBjcm9zcyBqb2luIChTRUxFQ1QgdmMuaW50ZXJJRCBmcm9tIHZhcmlhbnRzX2NhbmNlciBhcyB2YyBpbm5lciBqb2luIHNhbXBsZUluZm8gYXMgc2kgb24gc2kucG9zdHByb2NJRCA9IHZjLnR1bW9yX25hbWUgd2hlcmUgKCgodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgPj0gNTAgQU5EIChuX3JlZl9jb3VudCArIG5fYWx0X2NvdW50KSA+PSA1MCBBTkQgKGFubm92YXJfY29tcGxldGVfZ2Vub21pY3MgIT0gJycgT1IgYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCAoIGFubm92YXJfMTAwMGcgIT0gJycgT1IgYW5ub3Zhcl8xMDAwZyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCBqdWRnZW1lbnQgPSAxKSBPUiAoKCBhbm5vdmFyXzEwMDBnIGlzIE5VTEwgT1IgYW5ub3Zhcl8xMDAwZyA8IDAuMDEgKSBBTkQgKCBhbm5vdmFyX2VzcCBpcyBOVUxMIE9SIGFubm92YXJfZXNwIDwgMC4wMSkgQU5EIHRfYWx0X2NvdW50ID49IDEwIEFORCAgbl9hbHRfY291bnQgPCAzIEFORCBuX3JlZl9jb3VudCA+IDUwIEFORCB0dW1vcl9mID4gMC4wMSBBTkQgY29zbWljX2NlbnN1cyA9IDEgQU5EIChhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycgT1IgIGFubm92YXJfZnVuYyBSRUdFWFAgJ3NwbGljaW5nfHVwc3RyZWFtJykpKSBBTkQgc2kuc2FtcGxlSUQgaW4gKCciLCBtb2xlSURbMV0sICInICkpIGFzIHN1IHdoZXJlIHN1LmludGVySUQgPSB2Yy5pbnRlcklEIEFORCBzaS5wYWlySUQgPiAwIGFuZCBzaS5wYWlySUQgPCA5OTAwIEFORCAoKCh0X3JlZl9jb3VudCArIHRfYWx0X2NvdW50KSA+PSA1MCBBTkQgKG5fcmVmX2NvdW50ICsgbl9hbHRfY291bnQpID49IDUwIEFORCAoYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyAhPSAnJyBPUiBhbm5vdmFyX2NvbXBsZXRlX2dlbm9taWNzID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EICggYW5ub3Zhcl8xMDAwZyAhPSAnJyBPUiBhbm5vdmFyXzEwMDBnID0gMCBPUiBhbm5vdmFyX2NsaW52YXIgbGlrZSAnJWNsaW4lJykgQU5EIGp1ZGdlbWVudCA9IDEpIE9SICgoIGFubm92YXJfMTAwMGcgaXMgTlVMTCBPUiBhbm5vdmFyXzEwMDBnIDwgMC4wMSApIEFORCAoIGFubm92YXJfZXNwIGlzIE5VTEwgT1IgYW5ub3Zhcl9lc3AgPCAwLjAxKSBBTkQgdF9hbHRfY291bnQgPj0gMTAgQU5EICBuX2FsdF9jb3VudCA8IDMgQU5EIG5fcmVmX2NvdW50ID4gNTAgQU5EIHR1bW9yX2YgPiAwLjAxIEFORCBjb3NtaWNfY2Vuc3VzID0gMSBBTkQgKGFubm92YXJfZXhvbmljX2Z1bmMgUkVHRVhQICdub25zeW5vbnltb3VzIFNOVnxzdG9wZ2FpbiBTTlZ8c3RvcGxvc3MgU05WfHNwbGljaW5nJyBPUiAgYW5ub3Zhcl9mdW5jIFJFR0VYUCAnc3BsaWNpbmd8dXBzdHJlYW0nKSkpIGdyb3VwIGJ5IHZjLmludGVySUQgSEFWSU5HIGNvdW50KCopID4gMSkgdG1wIHdoZXJlIHRtcC5pbnRlcklEID0gdmMuaW50ZXJJRCBBTkQgc2kucGFpcklEID4gMCBhbmQgc2kucGFpcklEIDwgOTkwMCBhbmQgKCgodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgPj0gNTAgQU5EIChuX3JlZl9jb3VudCArIG5fYWx0X2NvdW50KSA+PSA1MCBBTkQgKGFubm92YXJfY29tcGxldGVfZ2Vub21pY3MgIT0gJycgT1IgYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCAoIGFubm92YXJfMTAwMGcgIT0gJycgT1IgYW5ub3Zhcl8xMDAwZyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVjbGluJScpIEFORCBqdWRnZW1lbnQgPSAxKSBPUiAoKCBhbm5vdmFyXzEwMDBnIGlzIE5VTEwgT1IgYW5ub3Zhcl8xMDAwZyA8IDAuMDEgKSBBTkQgKCBhbm5vdmFyX2VzcCBpcyBOVUxMIE9SIGFubm92YXJfZXNwIDwgMC4wMSkgQU5EIHRfYWx0X2NvdW50ID49IDEwIEFORCAgbl9hbHRfY291bnQgPCAzIEFORCBuX3JlZl9jb3VudCA+IDUwIEFORCB0dW1vcl9mID4gMC4wMSBBTkQgY29zbWljX2NlbnN1cyA9IDEgQU5EIChhbm5vdmFyX2V4b25pY19mdW5jIFJFR0VYUCAnbm9uc3lub255bW91cyBTTlZ8c3RvcGdhaW4gU05WfHN0b3Bsb3NzIFNOVnxzcGxpY2luZycgT1IgIGFubm92YXJfZnVuYyBSRUdFWFAgJ3NwbGljaW5nfHVwc3RyZWFtJykpKSIsIGNvbGxhcHNlID0gIiIsIHNlcCA9ICcnKQpjb21tb25TTlZzIDwtIG15c3FsUXVlcnkoY21kUXVlcnkpCgppbnRlclRyYW5zIDwtIGMoIjciID0gIlVua25vd24iLCAiNiIgPSAiQ2xhc3MtRSIsICI1IiA9ICJDbGFzcy1EIiwgIjQiID0gIkNsYXNzLUMiLCAiMyIgPSAiQ2xhc3MtQiIsICIyIiA9ICJDbGFzcy1BIiwgIjEiID0gIlVua25vd24iLCAiMCIgPSAiVW5rbm93biIpCgoKY29tbW9uU05WcyRpbnRlcnByZXRhdGlvbiA8LSBhcy5jaGFyYWN0ZXIoY29tbW9uU05WcyRpbnRlcnByZXRhdGlvbikKY29tbW9uU05WcyRpbnRlcnByZXRhdGlvbiA8LSByZXZhbHVlKGNvbW1vblNOVnMkaW50ZXJwcmV0YXRpb24sIGludGVyVHJhbnMpCmNvbW1vblNOVnMkY29zbWljX2NlbnN1c1tjb21tb25TTlZzJGNvc21pY19jZW5zdXMgPT0gMV0gPC0gIlQiCmNvbW1vblNOVnMkY29zbWljX2NlbnN1c1tjb21tb25TTlZzJGNvc21pY19jZW5zdXMgPT0gMF0gPC0gIkYiCmNvbW1vblNOVnMkTXV0YXRpb24gPC0gTlVMTAppbmRleCA8LSBjb21tb25TTlZzJGFhID09ICIiCmNvbW1vblNOVnMkTXV0YXRpb25baW5kZXhdIDwtIHBhc3RlKGNvbW1vblNOVnMkYW5ub3Zhcl9jaHJbaW5kZXhdLCAiOiIsIGNvbW1vblNOVnMkYW5ub3Zhcl9zdGFydFtpbmRleF0sICItIiwgY29tbW9uU05WcyRhbm5vdmFyX2VuZFtpbmRleF0gLCAiICgiLCBjb21tb25TTlZzJG11dGF0aW9uVHlwZVtpbmRleF0sICIpPGJyLz5mdW5jOiAiLCBjb21tb25TTlZzJGFubm92YXJfZnVuY1tpbmRleF0sICI8YnIvPmVucyBmdW5jOiAiLCBjb21tb25TTlZzJGFubm92YXJfZW5zX2Z1bmNbaW5kZXhdLCBzZXAgPSAnJykKaW5kZXggPC0gY29tbW9uU05WcyRhYSAhPSAiIgpjb21tb25TTlZzJE11dGF0aW9uW2luZGV4XSA8LSBwYXN0ZShjb21tb25TTlZzJGFubm92YXJfY2hyW2luZGV4XSwgIjoiLCBjb21tb25TTlZzJGFubm92YXJfc3RhcnRbaW5kZXhdLCAiLSIsIGNvbW1vblNOVnMkYW5ub3Zhcl9lbmRbaW5kZXhdICwgIiAoIiwgY29tbW9uU05WcyRtdXRhdGlvblR5cGVbaW5kZXhdLCAiKTxici8+ZnVuYzogIiwgY29tbW9uU05WcyRhbm5vdmFyX2Z1bmNbaW5kZXhdLCAiPGJyLz5lbnMgZnVuYzogIiwgY29tbW9uU05WcyRhbm5vdmFyX2Vuc19mdW5jW2luZGV4XSwgIjxici8+QUE6ICIsIGNvbW1vblNOVnMkYWFbaW5kZXhdLCBzZXAgPSAnJykKY29tbW9uU05WcyRNdVR5cGUgPC0gInN1YnN0aXR1dGlvbiIKcHJpbnRDb21tb25TTlZzIDwtIGNvbW1vblNOVnNbIHdoaWNoKGNvbW1vblNOVnMkc2FtcGxlSUQgPT0gbW9sZUlEWzFdKSwgXQpvdGhlckNvbW1vblNOVnMgPC0gY29tbW9uU05Wc1sgd2hpY2goY29tbW9uU05WcyRzYW1wbGVJRCAhPSBtb2xlSURbMV0pLCBdCgpzaWRGcmVxIDwtIGRhdGEuZnJhbWUodGFibGUob3RoZXJDb21tb25TTlZzJHNhbXBsZUlEKSkKc2lkRnJlcSA8LSBzaWRGcmVxW29yZGVyKC1zaWRGcmVxJEZyZXEpLCBdCm5hbWVzKHNpZEZyZXEpWzFdIDwtICJzYW1wbGVJRCIKc2lkRnJlcSRzYW1wbGVJRCA8LSBhcy5jaGFyYWN0ZXIoc2lkRnJlcSRzYW1wbGVJRCkKCm51bWNvbCA8LSAxMAppZiAobGVuZ3RoKHNpZEZyZXEkc2FtcGxlSUQpIDwgMTApIHsKICAgIG51bWNvbCA8LSBsZW5ndGgoc2lkRnJlcSRzYW1wbGVJRCkKfQpmaW5hbGNvbG5hbWVzIDwtIGMoJ0dlbmUnLCAnTXV0YXRpb25UeXBlJywgJ011dGF0aW9uJywgJ0ludGVycHJldGF0aW9uJywgJ0NhbmNlciBHZW5lJywgJ1ZBRicpCgpmb3IgKGkgaW4gMTpudW1jb2wpIHsKICBpbmRleDEgPC0gb3RoZXJDb21tb25TTlZzJHNhbXBsZUlEID09IHNpZEZyZXEkc2FtcGxlSURbaV0KICB0ZW1waWQgPC0gcGFzdGUoInNhbXBsZUlEIiwgaSAsIHNlcCA9ICcnKQogIGZpbmFsY29sbmFtZXMgPC0gYXBwZW5kKGZpbmFsY29sbmFtZXMsIHRlbXBpZCkKICBwcmludENvbW1vblNOVnNbdGVtcGlkXSA8LSBOQQogIHByaW50Q29tbW9uU05Wc1tbdGVtcGlkXV1bcHJpbnRDb21tb25TTlZzJGludGVySUQgJWluJSBvdGhlckNvbW1vblNOVnMkaW50ZXJJRFtpbmRleDFdXSA8LSBzaWRGcmVxJHNhbXBsZUlEW2ldCn0KcHJpbnRDb21tb25TTlZzJHNhbXBsZUNvdW50IDwtIGFwcGx5KHByaW50Q29tbW9uU05Wc1sxNzooMTYrbnVtY29sKV0sIDEsIGZ1bmN0aW9uKHgpIHN1bSghaXMubmEoeCkpKQpmaW5hbGNvbG5hbWVzIDwtIGFwcGVuZChmaW5hbGNvbG5hbWVzLCAic2FtcGxlQ291bnQiKQpwcmludENvbW1vblNOVnMgPC0gcHJpbnRDb21tb25TTlZzW2MoMSwgMTYsIDE1LCAxMSw5LDEyLCAxNzooMTcrbnVtY29sKSldCgpkYXRhdGFibGUocHJpbnRDb21tb25TTlZzLCBlc2NhcGUgPSAyLCByb3duYW1lcyA9IEZBTFNFLCAgY29sbmFtZXMgPSBmaW5hbGNvbG5hbWVzKQoKYGBgCgoKQ29tbW9uIElOREVMcyBiZXR3ZWVuIGN1cnJlbnQgaW52ZXN0aWdhdGVkIHNhbXBsZSBhbmQgb3RoZXIgc2FtcGxlczoKCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KY21kUXVlcnkgPC0gcGFzdGUoIlNFTEVDVCB2Yy5oZ25jX2dlbmUsIHZjLmFubm92YXJfY2hyLCB2Yy5hbm5vdmFyX3N0YXJ0LCB2Yy5hbm5vdmFyX2VuZCwgdmMuZ2F0a19tdXRhdGlvbl90eXBlLCB2Yy5hbm5vdmFyX2Z1bmMsIHZjLmFubm92YXJfZW5zX2Z1bmMsIHZjLmFhLCB2Yy5jb3NtaWNfY2Vuc3VzLCBzaS5zYW1wbGVJRCwgaXAuaW50ZXJwcmV0YXRpb24sIHZjLmdhdGtfdHVtb3VyX2xvZCwgdmMuaW50ZXJJRCwgQ09OQ0FUX1dTKCdfJywgdmMuaW50ZXJJRCwgdmMuYW5ub3Zhcl9yZWYsIHZjLmFubm92YXJfYWx0KSBhcyBuZXdpbnRlcklEIEZST00gaW5kZWxfY2FuY2VyIEFTIHZjIElOTkVSIEpPSU4gc2FtcGxlSW5mbyBBUyBzaSBPTiBzaS5wb3N0cHJvY0lEID0gdmMudHVtb3JfbmFtZSBMRUZUIEpPSU4gaW50ZXJwcmV0YXRpb25fY2FuY2VyIGFzIGlwIG9uIGlwLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIEFORCBpcC5pbnRlcklEID0gdmMuaW50ZXJJRCBjcm9zcyBqb2luIChzZWxlY3QgdmMuaW50ZXJJRCxjb3VudCgqKSBmcm9tIGluZGVsX2NhbmNlciBhcyB2YyBpbm5lciBqb2luIHNhbXBsZUluZm8gYXMgc2kgb24gc2kucG9zdHByb2NJRCA9IHZjLnR1bW9yX25hbWUgY3Jvc3Mgam9pbiAoU0VMRUNUIHZjLmludGVySUQgZnJvbSBpbmRlbF9jYW5jZXIgYXMgdmMgaW5uZXIgam9pbiBzYW1wbGVJbmZvIGFzIHNpIG9uIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIHdoZXJlIChnYXRrX211dGF0aW9uX3R5cGUgIT0gJ3N1YicgQU5EIGdhdGtfbm9ybWFsX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfYWxsZWxlX2ZyYWN0aW9uID49IDAuMDUgQU5EIChnYXRrX2ZpbHRlciA9ICdQQVNTJyBPUiAoKGdhdGtfZmlsdGVyIE5PVCBMSUtFICdjbHVzdGVyZWRfZXZlbnRzJyBPUiBnYXRrX2ZpbHRlciBOT1QgTElLRSAnaG9tb2xvZ291c19tYXBwaW5nX2V2ZW50JykgQU5EIGdhdGtfdHVtb3VyX2FsdF9jb3VudCA+PSA1IEFORCBnYXRrX25vcm1hbF9hbGxlbGVfZnJhY3Rpb24gPCAwLjAxKSkpIEFORCBzaS5zYW1wbGVJRCBpbiAoJyIsIG1vbGVJRFsxXSwgIicgKSkgYXMgc3Ugd2hlcmUgc3UuaW50ZXJJRCA9IHZjLmludGVySUQgQU5EIHNpLnBhaXJJRCA+IDAgQU5EIHNpLnBhaXJJRCA8IDk5MDAgQU5EIChnYXRrX211dGF0aW9uX3R5cGUgIT0gJ3N1YicgQU5EIGdhdGtfbm9ybWFsX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9kZXB0aCA+PSAzMCBBTkQgZ2F0a190dW1vdXJfYWxsZWxlX2ZyYWN0aW9uID49IDAuMDUgQU5EIChnYXRrX2ZpbHRlciA9ICdQQVNTJyBPUiAoKGdhdGtfZmlsdGVyIE5PVCBMSUtFICdjbHVzdGVyZWRfZXZlbnRzJyBPUiBnYXRrX2ZpbHRlciBOT1QgTElLRSAnaG9tb2xvZ291c19tYXBwaW5nX2V2ZW50JykgQU5EIGdhdGtfdHVtb3VyX2FsdF9jb3VudCA+PSA1IEFORCBnYXRrX25vcm1hbF9hbGxlbGVfZnJhY3Rpb24gPCAwLjAxKSkpIGdyb3VwIGJ5IHZjLmludGVySUQgSEFWSU5HIGNvdW50KCopID4gMSkgdG1wIHdoZXJlIHRtcC5pbnRlcklEID0gdmMuaW50ZXJJRCBBTkQgc2kucGFpcklEID4gMCBBTkQgc2kucGFpcklEIDwgOTkwMCBBTkQgKGdhdGtfbXV0YXRpb25fdHlwZSAhPSAnc3ViJyBBTkQgZ2F0a19ub3JtYWxfZGVwdGggPj0gMzAgQU5EIGdhdGtfdHVtb3VyX2RlcHRoID49IDMwIEFORCBnYXRrX3R1bW91cl9hbGxlbGVfZnJhY3Rpb24gPj0gMC4wNSBBTkQgKGdhdGtfZmlsdGVyID0gJ1BBU1MnIE9SICgoZ2F0a19maWx0ZXIgTk9UIExJS0UgJ2NsdXN0ZXJlZF9ldmVudHMnIE9SIGdhdGtfZmlsdGVyIE5PVCBMSUtFICdob21vbG9nb3VzX21hcHBpbmdfZXZlbnQnKSBBTkQgZ2F0a190dW1vdXJfYWx0X2NvdW50ID49IDUgQU5EIGdhdGtfbm9ybWFsX2FsbGVsZV9mcmFjdGlvbiA8IDAuMDEpKSkiLCBjb2xsYXBzZSA9ICIiLCBzZXAgPSAnJykKY29tbW9uU05WcyA8LSBteXNxbFF1ZXJ5KGNtZFF1ZXJ5KQoKaW50ZXJUcmFucyA8LSBjKCI3IiA9ICJVbmtub3duIiwgIjYiID0gIkNsYXNzLUUiLCAiNSIgPSAiQ2xhc3MtRCIsICI0IiA9ICJDbGFzcy1DIiwgIjMiID0gIkNsYXNzLUIiLCAiMiIgPSAiQ2xhc3MtQSIsICIxIiA9ICJVbmtub3duIiwgIjAiID0gIlVua25vd24iKQoKCmNvbW1vblNOVnMkaW50ZXJwcmV0YXRpb24gPC0gYXMuY2hhcmFjdGVyKGNvbW1vblNOVnMkaW50ZXJwcmV0YXRpb24pCmNvbW1vblNOVnMkaW50ZXJwcmV0YXRpb24gPC0gcmV2YWx1ZShjb21tb25TTlZzJGludGVycHJldGF0aW9uLCBpbnRlclRyYW5zKQpjb21tb25TTlZzJGNvc21pY19jZW5zdXNbY29tbW9uU05WcyRjb3NtaWNfY2Vuc3VzID09IDFdIDwtICJUIgpjb21tb25TTlZzJGNvc21pY19jZW5zdXNbY29tbW9uU05WcyRjb3NtaWNfY2Vuc3VzID09IDBdIDwtICJGIgpjb21tb25TTlZzJE11dGF0aW9uIDwtIE5VTEwKaW5kZXggPC0gY29tbW9uU05WcyRhYSA9PSAiIgpjb21tb25TTlZzJE11dGF0aW9uW2luZGV4XSA8LSBwYXN0ZShjb21tb25TTlZzJGFubm92YXJfY2hyW2luZGV4XSwgIjoiLCBjb21tb25TTlZzJGFubm92YXJfc3RhcnRbaW5kZXhdLCAiLSIsIGNvbW1vblNOVnMkYW5ub3Zhcl9lbmRbaW5kZXhdICwgIiAoIiwgY29tbW9uU05WcyRnYXRrX211dGF0aW9uX3R5cGVbaW5kZXhdLCAiKTxici8+ZnVuYzogIiwgY29tbW9uU05WcyRhbm5vdmFyX2Z1bmNbaW5kZXhdLCAiPGJyLz5lbnMgZnVuYzogIiwgY29tbW9uU05WcyRhbm5vdmFyX2Vuc19mdW5jW2luZGV4XSwgc2VwID0gJycpCmluZGV4IDwtIGNvbW1vblNOVnMkYWEgIT0gIiIKY29tbW9uU05WcyRNdXRhdGlvbltpbmRleF0gPC0gcGFzdGUoY29tbW9uU05WcyRhbm5vdmFyX2NocltpbmRleF0sICI6IiwgY29tbW9uU05WcyRhbm5vdmFyX3N0YXJ0W2luZGV4XSwgIi0iLCBjb21tb25TTlZzJGFubm92YXJfZW5kW2luZGV4XSAsICIgKCIsIGNvbW1vblNOVnMkZ2F0a19tdXRhdGlvbl90eXBlW2luZGV4XSwgIik8YnIvPmZ1bmM6ICIsIGNvbW1vblNOVnMkYW5ub3Zhcl9mdW5jW2luZGV4XSwgIjxici8+ZW5zIGZ1bmM6ICIsIGNvbW1vblNOVnMkYW5ub3Zhcl9lbnNfZnVuY1tpbmRleF0sICI8YnIvPkFBOiAiLCBjb21tb25TTlZzJGFhW2luZGV4XSwgc2VwID0gJycpCmNvbW1vblNOVnMkTXVUeXBlIDwtICJzdWJzdGl0dXRpb24iCnByaW50Q29tbW9uU05WcyA8LSBjb21tb25TTlZzWyB3aGljaChjb21tb25TTlZzJHNhbXBsZUlEID09IG1vbGVJRFsxXSksIF0Kb3RoZXJDb21tb25TTlZzIDwtIGNvbW1vblNOVnNbIHdoaWNoKGNvbW1vblNOVnMkc2FtcGxlSUQgIT0gbW9sZUlEWzFdKSwgXQoKc2lkRnJlcSA8LSBkYXRhLmZyYW1lKHRhYmxlKG90aGVyQ29tbW9uU05WcyRzYW1wbGVJRCkpCnNpZEZyZXEgPC0gc2lkRnJlcVtvcmRlcigtc2lkRnJlcSRGcmVxKSwgXQpuYW1lcyhzaWRGcmVxKVsxXSA8LSAic2FtcGxlSUQiCnNpZEZyZXEkc2FtcGxlSUQgPC0gYXMuY2hhcmFjdGVyKHNpZEZyZXEkc2FtcGxlSUQpCgpudW1jb2wgPC0gMTAKaWYgKGxlbmd0aChzaWRGcmVxJHNhbXBsZUlEKSA8IDEwKSB7CiAgICBudW1jb2wgPC0gbGVuZ3RoKHNpZEZyZXEkc2FtcGxlSUQpCn0KZmluYWxjb2xuYW1lcyA8LSBjKCdHZW5lJywgJ011dGF0aW9uVHlwZScsICdNdXRhdGlvbicsICdJbnRlcnByZXRhdGlvbicsICdDYW5jZXIgR2VuZScsICdWQUYnKQoKZm9yIChpIGluIDE6bnVtY29sKSB7CiAgaW5kZXgxIDwtIG90aGVyQ29tbW9uU05WcyRzYW1wbGVJRCA9PSBzaWRGcmVxJHNhbXBsZUlEW2ldCiAgdGVtcGlkIDwtIHBhc3RlKCJzYW1wbGVJRCIsIGkgLCBzZXAgPSAnJykKICBmaW5hbGNvbG5hbWVzIDwtIGFwcGVuZChmaW5hbGNvbG5hbWVzLCB0ZW1waWQpCiAgcHJpbnRDb21tb25TTlZzW3RlbXBpZF0gPC0gTkEKICBwcmludENvbW1vblNOVnNbW3RlbXBpZF1dW3ByaW50Q29tbW9uU05WcyRpbnRlcklEICVpbiUgb3RoZXJDb21tb25TTlZzJGludGVySURbaW5kZXgxXV0gPC0gc2lkRnJlcSRzYW1wbGVJRFtpXQp9CnByaW50Q29tbW9uU05WcyRzYW1wbGVDb3VudCA8LSBhcHBseShwcmludENvbW1vblNOVnNbMTc6KDE2K251bWNvbCldLCAxLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkKZmluYWxjb2xuYW1lcyA8LSBhcHBlbmQoZmluYWxjb2xuYW1lcywgInNhbXBsZUNvdW50IikKcHJpbnRDb21tb25TTlZzIDwtIHByaW50Q29tbW9uU05Wc1tjKDEsIDE2LCAxNSwgMTEsOSwxMiwgMTc6KDE3K251bWNvbCkpXQoKZGF0YXRhYmxlKHByaW50Q29tbW9uU05WcywgZXNjYXBlID0gMiwgcm93bmFtZXMgPSBGQUxTRSwgIGNvbG5hbWVzID0gZmluYWxjb2xuYW1lcykKCmBgYAoKCk11dGF0aW9uIEJ1cmRlbiBQbG90CmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KbXV0YXRpb25CdXJkZW4gPC0gbXlzcWxRdWVyeSgiU0VMRUNUIGlkcy5zYW1wbGVJRCwgc252LmNvdW50IGFzIHNudkgsIHNudi5jb3VudC8zLjI0NTQ0NiBBUyBNdXRhdGlvbkJ1cmRlbiBGUk9NICggc2VsZWN0IHNhbXBsZUlELHBvc3Rwcm9jSUQgZnJvbSBzYW1wbGVJbmZvIEFTIHNpIElOTkVSIEpPSU4gdmFyaWFudHNfY2FuY2VyIEFTIHZjIE9OIHNpLnBvc3Rwcm9jSUQgPSB2Yy50dW1vcl9uYW1lIFdIRVJFIHNpLnBhaXJJRCA+MSBBTkQgc2kucGFpcklEIDwgOTkwMCBHUk9VUCBCWSBzYW1wbGVJRCkgaWRzIExFRlQgSk9JTiAoU0VMRUNUIHR1bW9yX25hbWUsIGNvdW50KCopIEFTIGNvdW50IEZST00gdmFyaWFudHNfY2FuY2VyIHZjIHdoZXJlICgodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgPj0gNTAgQU5EIChuX3JlZl9jb3VudCArIG5fYWx0X2NvdW50KSA+PSA1MCBBTkQgKGFubm92YXJfY29tcGxldGVfZ2Vub21pY3MgIT0gJycgT1IgYW5ub3Zhcl9jb21wbGV0ZV9nZW5vbWljcyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVDTElOJScpIEFORCAoIGFubm92YXJfMTAwMGcgIT0gJycgT1IgYW5ub3Zhcl8xMDAwZyA9IDAgT1IgYW5ub3Zhcl9jbGludmFyIGxpa2UgJyVDTElOJScpIEFORCBqdWRnZW1lbnQgPSAxKSBhbmQgbGVuZ3RoKGluX2NwYW5lbCkgPiA0IEdST1VQIGJ5IHR1bW9yX25hbWUgKSBzbnYgT04gaWRzLnBvc3Rwcm9jSUQgPSBzbnYudHVtb3JfbmFtZSBPUkRFUiBCWSBzbnZIIERFU0MiKQoKbXV0YXRpb25CdXJkZW4gPC0gbXV0YXRlKG11dGF0aW9uQnVyZGVuLCBjdXJJbnYgPSBzYW1wbGVJRCAhPSBtb2xlSURbMV0gKQptdXRhdGlvbkJ1cmRlbiA8LSBtdXRhdGlvbkJ1cmRlblsgd2hpY2goISBpcy5uYShtdXRhdGlvbkJ1cmRlbiRzbnZIKSksIF0KI211dGF0aW9uQnVyZGVuICU+JSBnZ3Bsb3QoYWVzKHg9c2FtcGxlSUQsIHk9TXV0YXRpb25CdXJkZW4sIGZpbGwgPSBjdXJJbnYpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aD0uNykgKyBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IG11dGF0aW9uQnVyZGVuJHNhbXBsZUlEKSArICBzY2FsZV95X2xvZzEwKCBicmVha3MgPSBzY2FsZXM6OnRyYW5zX2JyZWFrcygibG9nMTAiLCBmdW5jdGlvbih4KSAxMF54KSwgbGFiZWxzID0gc2NhbGVzOjp0cmFuc19mb3JtYXQoImxvZzEwIiwgc2NhbGVzOjptYXRoX2Zvcm1hdCgxMF4ueCkpICkgKyB0aGVtZV9idygpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gNiksIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICArIGxhYnMoeSA9ICJNdXRhdGlvbiBCdXJkZW4iKQoKbXV0YXRpb25CdXJkZW4gJT4lIGdncGxvdChhZXMoeD1zYW1wbGVJRCwgeT1NdXRhdGlvbkJ1cmRlbioxMCwgZmlsbCA9IGN1ckludikpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoPS43KSArIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gbXV0YXRpb25CdXJkZW4kc2FtcGxlSUQpICsgc2NhbGVfeV9sb2cxMChicmVha3MgPSBjKDEsIDEwLCAxMDAsIDEwMDApLCBsYWJlbHMgPSBjKCIwIiwgIjEiLCAiMTAiLCAiMTAwIikpICsgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDYpLCBsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAgKyBsYWJzKHkgPSAiTXV0YXRpb24gQnVyZGVuIikKCmBgYAoKCgpPdmVyYWxsIENvdmVyYWdlCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KY21kUXVlcnkgPC0gcGFzdGUoIlNFTEVDVCBwZXJiYXNlc0Fib3ZlMVhHUCBhcyAnJT4xWCcscGVyYmFzZXNBYm92ZTEwWEdQIGFzICclPjUwWCcscGVyYmFzZXNBYm92ZTIwWEdQIGFzICclPjIwMFgnLCBwZXJiYXNlc0Fib3ZlMzBYR1AgYXMgJyU+NTAwWCcgRlJPTSBzYW1wbGVJbmZvIFdIRVJFIHNhbXBsZUlEIGluICgnIiwgbW9sZUlEWzFdLCAiJykiLCBjb2xsYXBzZSA9ICIiLCBzZXAgPSAnJykKY3ZnU3VtIDwtIG15c3FsUXVlcnkoY21kUXVlcnkpCiNiYXJwbG90KGMoY3ZnU3VtJGAlPjFYYFsxXSwgY3ZnU3VtJGAlPjUwWGBbMV0sIGN2Z1N1bSRgJT4yMDBYYFsxXSwgY3ZnU3VtJGAlPjUwMFhgWzFdKSwgbWFpbj0iT3ZlcmFsbCBDb3ZlcmFnZSIsIG5hbWVzLmFyZyA9YygiJT4xWCIsICIlPjUwWCIsICIlPjIwMFgiLCAiJT41MDBYIiksIHlsYWI9IlBlcmNlbnRhZ2Ugb2YgQmFzZXMiLCBjb2wgPSAiY3lhbiIpCgpjdmdCYXJQbG90IDwtIGRhdGEuZnJhbWUoIGl0ZW1zID0gbmFtZXMoY3ZnU3VtKSwgdmFsdWVzID0gYyhjdmdTdW0kYCU+MVhgWzFdLCBjdmdTdW0kYCU+NTBYYFsxXSwgY3ZnU3VtJGAlPjIwMFhgWzFdLCBjdmdTdW0kYCU+NTAwWGBbMV0pKQpjdmdCYXJQbG90JGhsaW5lIDwtIGMoTkEsIDk3LCA5NSwgNzUpCgppdGVtb3JkZXIgPC0gYygiJT4xWCIsICAiJT41MFgiLCAgIiU+MjAwWCIsICIlPjUwMFgiKQoKY3ZnQmFyUGxvdCAlPiUgZ2dwbG90KGFlcyh4PWl0ZW1zLCB5PXZhbHVlcykpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoPS41LCBmaWxsPSJzdGVlbGJsdWUiKSArIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gaXRlbW9yZGVyKSArIGdlb21fdGV4dChhZXMobGFiZWw9dmFsdWVzKSwgdmp1c3Q9LS41LCBjb2xvcj0iYmxhY2siLCBzaXplPTMuNSkgICsgIHRoZW1lX21pbmltYWwoKSArIGdlb21fZXJyb3JiYXIoYWVzKHdpZHRoPS41LCB5bWF4PWhsaW5lLCB5bWluPWhsaW5lKSwgY29sb3VyPSIjQUEwMDAwIikKYGBgCgoKCgpleG9uIGNvdmVyYWdlIHBsb3QKYGBge3IgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2LCB3YXJuaW5nID0gRkFMU0V9Cm9wdGlvbnMod2Fybj0tMSkKY21kUXVlcnkgPC0gcGFzdGUoIlNFTEVDVCBlYy5jaHIsZWMuc3RhcnQsZWMuc3RvcCxlYy5nZW5lX2V4b24sZWMuY292ZXJhZ2UgRlJPTSBleG9uQ292IEFTIGVjIElOTkVSIEpPSU4gc2FtcGxlSW5mbyBBUyBzaSBPTiBzaS5wb3N0cHJvY0lEID0gZWMucG9zdHByb2NJRCBXSEVSRSBzaS5zYW1wbGVJRCA9JyIsIG1vbGVJRFsxXSwgIiciLCBjb2xsYXBzZSA9ICIiLCBzZXAgPSAnJykKY292cyA8LSBteXNxbFF1ZXJ5KGNtZFF1ZXJ5KQpibGFja2xpc3QgPC0gbXlzcWxRdWVyeSgiU0VMRUNUIGxvd0N2Z0V4b24gRlJPTSBsb3dDdmdFeG9uIFdIRVJFIGNhcHR1cmVLaXQgPSAnY2FuY2VyJyIpCmJsYWNrbGlzdCA8LSB1bmxpc3Qoc3Ryc3BsaXQoYmxhY2tsaXN0JGxvd0N2Z0V4b24sICI7ICIpKQpjb3ZzIDwtIGNvdnNbICEgY292cyRnZW5lX2V4b24gJWluJSBibGFja2xpc3QsIF0KCmN2Z1N1bSA8LSBteXNxbFF1ZXJ5KHBhc3RlKCJTRUxFQ1QgbWVhbkN2Z0dQLHBlcmJhc2VzQWJvdmUzMFhHUCBGUk9NIHNhbXBsZUluZm8gV0hFUkUgc2FtcGxlSUQgPSAnIiwgbW9sZUlEWzFdLCAiJyIsIHNlcCA9ICcnKSkKCmNvdnMgPSBtdXRhdGUoY292cywgZ29vZF9jb3YgPSBjb3ZlcmFnZSA+PSA1MCApCmNvdnMgPSBtdXRhdGUoY292cywgYmFkX2NvdiA9IGNvdmVyYWdlIDwgNTApCmNvdnMkR2VuZSA8LSBzdWIoIi0uKiIsICIiLCBjb3ZzJGdlbmVfZXhvbikKY292cyRFeG9uIDwtIHN1YigiLiotIiwgIiIsIGNvdnMkZ2VuZV9leG9uKQpjb3ZzJHRydWVjIDwtIHVuc3BsaXQobGFwcGx5KHNwbGl0KGNvdnMsIGNvdnNbYygiR2VuZSIsImJhZF9jb3YiKV0pLCBucm93KSwgY292c1tjKCJHZW5lIildKQpjb3ZzJGZhbHNlYyA8LSB1bnNwbGl0KGxhcHBseShzcGxpdChjb3ZzLCBjb3ZzW2MoIkdlbmUiLCJnb29kX2NvdiIpXSksIG5yb3cpLCBjb3ZzW2MoIkdlbmUiKV0pCmNvdnMkR2VuZV9DdmcgPC0gcGFzdGUoY292cyRHZW5lLCAiICgiLCBjb3ZzJHRydWVjLCAiLyIsIGNvdnMkZmFsc2VjLCAiKSIsIHNlcCA9ICcnKQpjb3ZzJEdlbmVfQ3ZnIDwtIGZhY3Rvcihjb3ZzJEdlbmVfQ3ZnLCBsZXZlbHMgPSBjb3ZzJEdlbmVfQ3ZnW29yZGVyKGNvdnMkZ29vZF9jb3YpXSkKCmNvdnMgJT4lIGdncGxvdChhZXMoRXhvbiwiY3JhcCIsY29sb3IgID0gZ29vZF9jb3YpKSAgKyBnZW9tX3BvaW50KHNoYXBlID0gMykgKyBmYWNldF93cmFwKCAgfiBHZW5lX0N2Zywgc2NhbGVzID0gImZyZWUiKSArIGdndGl0bGUocGFzdGUoJ0NvdmVyYWdlIG9mIGFsbCBleG9ucyBpbiB0aGUgY2FuY2VyIHBhbmVsIGZvciBzYW1wbGUgJywgbW9sZUlEWzFdLCBzZXA9JycpKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjEsIDAsIDAsIC0wLjgpLCAibGluZSIpLCBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLCAibGluZXMiKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTUsIGhqdXN0ID0gMCksIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDIpKSArIHRoZW1lKGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApKSArIHRoZW1lKGF4aXMubGluZS54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ICA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnkgID0gZWxlbWVudF9ibGFuaygpKSArIHhsYWIocGFzdGUoIkF2ZXJhZ2UgQ292ZXJhZ2UgaXMgIiwgY3ZnU3VtJG1lYW5DdmdHUFsxXSwgIlgsICh3LyAiLCBjdmdTdW0kcGVyYmFzZXNBYm92ZTMwWEdQWzFdLCAiJSBiYXNlcyA+IDUwMFgpXG4iLCAiRXhvbnMgQ292ZXJhZ2Ugdy8gIiwgbnJvdyhjb3ZzW2NvdnMkZ29vZF9jb3YgPT0gVFJVRSxdKSAsICIgPiA1MFgsICIsIG5yb3coY292c1tjb3ZzJGJhZF9jb3YgPT0gVFJVRSxdKSwgIiA8IDUwWCIsIHNlcCA9ICcnKSkgCmBgYAoKCkNvdmVyYWdlIGRpc3RyaWJ1dGlvbiBvZiBFeG9ucyBvZiBhbGwgZ2VuZXMKYGBge3IgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2LCB3YXJuaW5nID0gRkFMU0V9Cgpjb3ZfdHVtb3IgPC0gbXlzcWxRdWVyeSgiU0VMRUNUIGdlbmVfZXhvbiwgY292ZXJhZ2UsIHBhaXJJRCwgQ09OQ0FUKGdlbmVfZXhvbiwgJ18nLCBwYWlySUQpIEFTIGlkX1VuaXEgRlJPTSBleG9uQ292IElOTkVSIEpPSU4gc2FtcGxlSW5mbyBPTiBzYW1wbGVJbmZvLnBvc3Rwcm9jSUQgPSBleG9uQ292LnBvc3Rwcm9jSUQgd2hlcmUgc2FtcGxlVHlwZSA9ICd0dW1vcicgQU5EIHBhaXJJRCA+IDAgQU5EIHBhaXJJRCA8IDk5MDAgQU5EIGN1cnJlbnRTdGF0dXMgPiA3IEFORCBjb3ZlcmFnZSA+IDAiKQpjb3Zfbm9ybWFsIDwtIG15c3FsUXVlcnkoIlNFTEVDVCBnZW5lX2V4b24sIGNvdmVyYWdlLCBwYWlySUQsIENPTkNBVChnZW5lX2V4b24sICdfJywgcGFpcklEKSBBUyBpZF9VbmlxX24gRlJPTSBleG9uQ292IElOTkVSIEpPSU4gc2FtcGxlSW5mbyBPTiBzYW1wbGVJbmZvLnBvc3Rwcm9jSUQgPSBleG9uQ292LnBvc3Rwcm9jSUQgd2hlcmUgc2FtcGxlVHlwZSA9ICdub3JtYWwnIEFORCBwYWlySUQgPiAwIEFORCBwYWlySUQgPCA5OTAwIEFORCBjdXJyZW50U3RhdHVzID4gNyBhbmQgY292ZXJhZ2UgPiAwIEdST1VQIEJZIHBhaXJJRCxnZW5lX2V4b24gT1JERVIgYnkgc2FtcGxlSW5mby5wb3N0cHJvY0lEIERFU0MiKQoKYmxhY2tsaXN0IDwtIG15c3FsUXVlcnkoIlNFTEVDVCBsb3dDdmdFeG9uIEZST00gbG93Q3ZnRXhvbiBXSEVSRSBjYXB0dXJlS2l0ID0gJ2NhbmNlciciKQpibGFja2xpc3QgPC0gdW5saXN0KHN0cnNwbGl0KGJsYWNrbGlzdCRsb3dDdmdFeG9uLCAiOyAiKSkKY292X3R1bW9yIDwtIGNvdl90dW1vclsgISBjb3ZfdHVtb3IkZ2VuZV9leG9uICVpbiUgYmxhY2tsaXN0LCBdCmNvdl9ub3JtYWwgPC0gY292X25vcm1hbFsgISBjb3Zfbm9ybWFsJGdlbmVfZXhvbiAlaW4lIGJsYWNrbGlzdCwgXQoKY292NHBsb3QgPC0gbWVyZ2UoY292X3R1bW9yLCBjb3Zfbm9ybWFsLCBieS54PSdpZF9VbmlxJywgYnkueT0naWRfVW5pcV9uJylbLCBjKDIsNSwzLDYsNCldCmNvdjRwbG90JGdlbmVfZXhvbi55IDwtIHN1YigiLS4qIiwgIiIsIGNvdjRwbG90JGdlbmVfZXhvbi55KQpjb3Y0cGxvdCA8LSBtdXRhdGUoY292NHBsb3QsIGxvZ19yYXRpbyA9IGxvZzIoY292NHBsb3QkY292ZXJhZ2UueCAvIGNvdjRwbG90JGNvdmVyYWdlLnkpKQoKZ2dwbG90KGNvdjRwbG90LCBhZXMoeD1nZW5lX2V4b24ueSwgeT1sb2dfcmF0aW8pKSArIGdlb21fYm94cGxvdCgpCgpgYGAKCgpDb3ZlcmFnZSBkaXN0cmlidXRpb24gb2YgRXhvbnMgb2YgY2VydGFpbiBnZW5lCmBgYHtyfQoKY292X215Y24gPC0gbXlzcWxRdWVyeSgiU0VMRUNUIENPTkNBVChnZW5lX2V4b24sICdcbicsIGNociwgJzonLCBzdGFydCwgJy0nLCBzdG9wKSBBUyBnZW5lX2V4b24sIGNvdmVyYWdlLCBzYW1wbGVJRCBGUk9NIGV4b25Db3YgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIE9OIHNhbXBsZUluZm8ucG9zdHByb2NJRCA9IGV4b25Db3YucG9zdHByb2NJRCB3aGVyZSBnZW5lX2V4b24gbGlrZSAnTVlDTi0lJyBBTkQgc2FtcGxlVHlwZSA9ICd0dW1vcicgQU5EIGN1cnJlbnRTdGF0dXMgPiA3IikKY292X215Y24kaGxpbmUgPC0gY292X215Y25bIHdoaWNoKGNvdl9teWNuJHNhbXBsZUlEID09IG1vbGVJRFsxXSksXSRjb3ZlcmFnZQoKZ2dwbG90KGNvdl9teWNuLCBhZXMoeD0gZ2VuZV9leG9uLCB5PWNvdmVyYWdlKSkgKyBnZW9tX2JveHBsb3QoKSArICBzY2FsZV95X2xvZzEwKCBicmVha3MgPSBzY2FsZXM6OnRyYW5zX2JyZWFrcygibG9nMTAiLCBmdW5jdGlvbih4KSAxMF54KSwgbGFiZWxzID0gc2NhbGVzOjp0cmFuc19mb3JtYXQoImxvZzEwIiwgc2NhbGVzOjptYXRoX2Zvcm1hdCgxMF4ueCkpICkgKyBnZW9tX2Vycm9yYmFyKGFlcyh3aWR0aD0uNSwgeW1heD1obGluZSwgeW1pbj1obGluZSksIGNvbG91cj0iI0FBMDAwMCIpCgpgYGAKCgoKQ292ZXJhZ2UgZGlzdHJpYnV0aW9uIG9mIEV4b25zIG9mIGNlcnRhaW4gZ2VuZQoKYGBge3J9Cgpjb3ZfZ2F0YTEgPC0gbXlzcWxRdWVyeSgiU0VMRUNUIENPTkNBVChnZW5lX2V4b24sICdcbicsIGNociwgJzonLCBzdGFydCwgJy0nLCBzdG9wKSBBUyBnZW5lX2V4b24sIGNvdmVyYWdlLCBzYW1wbGVJRCBGUk9NIGV4b25Db3YgSU5ORVIgSk9JTiBzYW1wbGVJbmZvIE9OIHNhbXBsZUluZm8ucG9zdHByb2NJRCA9IGV4b25Db3YucG9zdHByb2NJRCB3aGVyZSBnZW5lX2V4b24gbGlrZSAnR0FUQTEtJScgQU5EIHNhbXBsZVR5cGUgPSAndHVtb3InIEFORCBjdXJyZW50U3RhdHVzID4gNyIpCmNvdl9nYXRhMSRobGluZSA8LSBjb3ZfZ2F0YTFbIHdoaWNoKGNvdl9nYXRhMSRzYW1wbGVJRCA9PSBtb2xlSURbMV0pLF0kY292ZXJhZ2UKCmdncGxvdChjb3ZfZ2F0YTEsIGFlcyh4PSBnZW5lX2V4b24sIHk9Y292ZXJhZ2UpKSArIGdlb21fYm94cGxvdCgpICsgIHNjYWxlX3lfbG9nMTAoIGJyZWFrcyA9IHNjYWxlczo6dHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLCBsYWJlbHMgPSBzY2FsZXM6OnRyYW5zX2Zvcm1hdCgibG9nMTAiLCBzY2FsZXM6Om1hdGhfZm9ybWF0KDEwXi54KSkgKSAgKyBnZW9tX2Vycm9yYmFyKGFlcyh3aWR0aD0uNSwgeW1heD1obGluZSwgeW1pbj1obGluZSksIGNvbG91cj0iI0FBMDAwMCIpCgpgYGAKCg==